From 4b67747e3e6344ee23c8939719cc75d4e4bae3c0 Mon Sep 17 00:00:00 2001 From: Cyrus <24922493+cyrus-dev@users.noreply.github.com> Date: Thu, 21 Sep 2023 15:02:24 -0400 Subject: [PATCH 01/15] Updated the bulk of the code that does all provisioning from a client. There is some parsed functionality to ease sorting through the changes and updates needed in the future. --- HIRS_AttestationCA/build.gradle | 1 + .../persist/PCRQuoteValidator.java | 223 --- .../ReferenceDigestValueRepository.java | 5 +- .../manager/ReferenceManifestRepository.java | 2 + .../certificate/PlatformCredential.java | 2 + .../provision/CertificateRequestHandler.java | 1 + .../provision/IdentityClaimHandler.java | 9 +- .../service/SupplyChainValidationService.java | 366 +++- .../SupplyChainValidationServiceImpl.java | 377 ---- .../attestationca/persist/util}/PciIds.java | 2 +- .../validation/CredentialValidator.java | 223 ++- .../persist/validation/PcrValidator.java | 26 + .../SupplyChainCredentialValidator.java | 496 ++++- ...eferenceManifestDetailsPageController.java | 28 +- .../utils/CertificateStringMapBuilder.java | 1 + .../utils/SupplyChainCredentialValidator.java | 1766 ----------------- .../utils/tpm/eventlog/uefi/UefiGuid.java | 2 +- 17 files changed, 1055 insertions(+), 2475 deletions(-) delete mode 100644 HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/PCRQuoteValidator.java delete mode 100644 HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/service/SupplyChainValidationServiceImpl.java rename {HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/utils => HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/util}/PciIds.java (99%) delete mode 100644 HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/utils/SupplyChainCredentialValidator.java diff --git a/HIRS_AttestationCA/build.gradle b/HIRS_AttestationCA/build.gradle index 31806ddd..e9427645 100644 --- a/HIRS_AttestationCA/build.gradle +++ b/HIRS_AttestationCA/build.gradle @@ -36,6 +36,7 @@ dependencies { implementation libs.jakarta.api implementation libs.jakarta.xml implementation libs.hibernate.core + implementation libs.pci implementation libs.guava implementation libs.jackson.core implementation libs.jackson.databind diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/PCRQuoteValidator.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/PCRQuoteValidator.java deleted file mode 100644 index 431dacf6..00000000 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/PCRQuoteValidator.java +++ /dev/null @@ -1,223 +0,0 @@ -package hirs.attestationca.persist; - -import hirs.attestationca.persist.entity.userdefined.PolicySettings; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; -import lombok.extern.log4j.Log4j2; - -/** - * The class handles the flags that ignore certain PCRs for validation. - */ -@Log4j2 -@NoArgsConstructor -public class PCRQuoteValidator { - - /** - * Minimum possible value for a PCR ID. This is 0. - */ - public static final int MIN_PCR_ID = 0; - - /** - * Maximum possible value for a PCR ID. This is 23. - */ - public static final int MAX_PCR_ID = 23; - - private static final int NUM_TO_SKIP = 1; - private static final int NUM_OF_TBOOT_PCR = 3; - // PCR 5-16 - private static final int PXE_PCR_START = 5; - private static final int PXE_PCR_END = 16; - // PCR 10 - private static final int IMA_PCR = 10; - // PCR 17-19 - private static final int TBOOT_PCR_START = 17; - private static final int TBOOT_PCR_END = 19; - // PCR 5 - private static final int GPT_PCR = 5; - private static final int IMA_MASK = 0xfffbff; - - // Event Log Event Types - private static final String EVT_EFI_BOOT = "EV_EFI_BOOT_SERVICES_APPLICATION"; - private static final String EVT_EFI_VAR = "EV_EFI_VARIABLE_BOOT"; - private static final String EVT_EFI_GPT = "EV_EFI_GPT_EVENT"; - private static final String EVT_EFI_CFG = "EV_EFI_VARIABLE_DRIVER_CONFIG"; - - private String[] baselinePCRS = new String[MAX_PCR_ID + 1]; - @Getter - @Setter - private PolicySettings settings; - - /** - * Constructor to parse PCR values. - * @param pcrValues pcrValues RIM provided baseline PCRs - * @param settings settings for the supply chain portal settings for provisioning - */ - public PCRQuoteValidator(final String[] pcrValues, - final PolicySettings settings) { - if (pcrValues != null) { - baselinePCRS = new String[MAX_PCR_ID + 1]; - for (int i = 0; i <= MAX_PCR_ID; i++) { - baselinePCRS[i] = pcrValues[i]; - } - } - - this.settings = settings; - } - - /** - * Getter for the array of baseline PCRs. - * @return instance of the PCRs. - */ - public String[] getBaselinePCRS() { - return baselinePCRS.clone(); - } - - /** - * Setter for the array of baseline PCRs. - * @param baselinePCRS instance of the PCRs. - */ - public void setBaselinePCRS(final String[] baselinePCRS) { - this.baselinePCRS = baselinePCRS.clone(); - } - - /** - * Compares the baseline pcr list and the quote pcr list. If the - * ignore flags are set, 10 and 17-19 will be skipped for comparison. - * - * @param storedPCRS non-baseline pcr list - * @return a StringBuilder that is empty if everything passes. - */ - public StringBuilder validatePCRS(final String[] storedPCRS) { - StringBuilder sb = new StringBuilder(); - String failureMsg = "PCR %d does not match%n"; - if (storedPCRS[0] == null || storedPCRS[0].isEmpty()) { - sb.append("failureMsg"); - } else { - for (int i = 0; i <= MAX_PCR_ID; i++) { - if (settings.isIgnoreImaEnabled() && i == IMA_PCR) { - log.info("PCR Policy IMA Ignore enabled."); - i += NUM_TO_SKIP; - } - - if (settings.isIgnoretBootEnabled() && i == TBOOT_PCR_START) { - log.info("PCR Policy TBoot Ignore enabled."); - i += NUM_OF_TBOOT_PCR; - } - - if (settings.isIgnoreGptEnabled() && i == GPT_PCR) { - log.info("PCR Policy GPT Ignore enabled."); - i += NUM_TO_SKIP; - } - - if (!baselinePCRS[i].equals(storedPCRS[i])) { - //error - log.error(String.format("%s =/= %s", baselinePCRS[i], storedPCRS[i])); - sb.append(String.format(failureMsg, i)); - } - } - } - - return sb; - } - - /** - * Checks that the expected FM events occurring. There are policy options that - * will ignore certain PCRs, Event Types and Event Variables present. - * @param tcgMeasurementLog Measurement log from the client - * @param eventValueMap The events stored as baseline to compare - * @return the events that didn't pass - */ -// public List validateTpmEvents(final TCGEventLog tcgMeasurementLog, -// final Map eventValueMap) { -// List tpmPcrEvents = new LinkedList<>(); -// for (TpmPcrEvent tpe : tcgMeasurementLog.getEventList()) { -// if (enableIgnoreIma && tpe.getPcrIndex() == IMA_PCR) { -// log.info(String.format("IMA Ignored -> %s", tpe)); -// } else if (enableIgnoretBoot && (tpe.getPcrIndex() >= TBOOT_PCR_START -// && tpe.getPcrIndex() <= TBOOT_PCR_END)) { -// log.info(String.format("TBOOT Ignored -> %s", tpe)); -// } else if (enableIgnoreOsEvt && (tpe.getPcrIndex() >= PXE_PCR_START -// && tpe.getPcrIndex() <= PXE_PCR_END)) { -// log.info(String.format("OS Evt Ignored -> %s", tpe)); -// } else { -// if (enableIgnoreGpt && tpe.getEventTypeStr().contains(EVT_EFI_GPT)) { -// log.info(String.format("GPT Ignored -> %s", tpe)); -// } else if (enableIgnoreOsEvt && (tpe.getEventTypeStr().contains(EVT_EFI_BOOT) -// || tpe.getEventTypeStr().contains(EVT_EFI_VAR))) { -// log.info(String.format("OS Evt Ignored -> %s", tpe)); -// } else if (enableIgnoreOsEvt && (tpe.getEventTypeStr().contains(EVT_EFI_CFG) -// && tpe.getEventContentStr().contains("SecureBoot"))) { -// log.info(String.format("OS Evt Config Ignored -> %s", tpe)); -// } else { -// if (!eventValueMap.containsKey(tpe.getEventDigestStr())) { -// tpmPcrEvents.add(tpe); -// } -// } -// } -// } -// -// return tpmPcrEvents; -// } - - /** - * Compares hashes to validate the quote from the client. - * - * @param tpmQuote the provided quote - * @param storedPCRS values from the RIM file - * @return true if validated, false if not - */ -// public boolean validateQuote(final byte[] tpmQuote, final String[] storedPCRS) { -// System.out.println("Validating quote from associated device."); -// boolean validated = false; -// short localityAtRelease = 0; -// String quoteString = new String(tpmQuote, StandardCharsets.UTF_8); -// int pcrMaskSelection = PcrSelection.ALL_PCRS_ON; -// -// if (enableIgnoreIma) { -// pcrMaskSelection = IMA_MASK; -// } -// -// ArrayList measurements = new ArrayList<>(); -// -// try { -// for (int i = 0; i < storedPcrs.length; i++) { -// if (i == IMA_PCR && enableIgnoreIma) { -// log.info("Ignore IMA PCR policy is enabled."); -// } else { -// measurements.add(new TPMMeasurementRecord(i, storedPcrs[i])); -// } -// } -// } catch (DecoderException deEx) { -// //error -// System.out.println(deEx); -// } -// -// PcrSelection pcrSelection = new PcrSelection(pcrMaskSelection); -// PcrComposite pcrComposite = new PcrComposite(pcrSelection); -// PcrInfoShort pcrInfoShort = new PcrInfoShort(pcrSelection, -// localityAtRelease, -// tpmQuote, pcrComposite); -// -// try { -// /** -// * The calculated string is being used in the contains method -// * because the TPM Quote's hash isn't just for PCR values, -// * it contains the calculated digest of the PCRs, along with -// * other information. -// */ -// String calculatedString = Hex.encodeHexString( -// pcrInfoShort.getCalculatedDigest()); -// validated = quoteString.contains(calculatedString); -// if (!validated) { -// // warn -// System.out.println(calculatedString + " not found in " + quoteString); -// } -// } catch (NoSuchAlgorithmException naEx) { -// // error -// System.out.println(naEx); -// } -// -// return validated; -// } -} diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/manager/ReferenceDigestValueRepository.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/manager/ReferenceDigestValueRepository.java index 24be3dd5..c228a587 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/manager/ReferenceDigestValueRepository.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/manager/ReferenceDigestValueRepository.java @@ -11,12 +11,9 @@ import java.util.UUID; @Repository public interface ReferenceDigestValueRepository extends JpaRepository { - @Query(value = "SELECT * FROM ReferenceDigestValue", nativeQuery = true) - List listAll(); List findByModel(String model); List findByManufacturer(String manufacturer); - @Query(value = "SELECT * FROM ReferenceDigestValue WHERE baseRimId = '?1' OR supportRimId = '?1'", nativeQuery = true) - List getValuesByRimId(UUID associatedRimId); + List findValuesByBaseRimId(UUID associatedRimId); List findBySupportRimId(UUID supportRimId); List findBySupportRimHash(String supportRimHash); List findByManufacturerAndModel(String manufacturer, String model); diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/manager/ReferenceManifestRepository.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/manager/ReferenceManifestRepository.java index b74516bf..eb0892b2 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/manager/ReferenceManifestRepository.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/manager/ReferenceManifestRepository.java @@ -39,4 +39,6 @@ public interface ReferenceManifestRepository extends JpaRepository getSupportByManufacturerModel(String manufacturer, String model); + @Query(value = "SELECT * FROM ReferenceManifest WHERE platformModel = ?1 AND DTYPE = 'EventLogMeasurements'", nativeQuery = true) + EventLogMeasurements getLogByModel(String model); } diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/PlatformCredential.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/PlatformCredential.java index 52b6769e..4956723f 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/PlatformCredential.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/PlatformCredential.java @@ -176,6 +176,8 @@ public class PlatformCredential extends DeviceAssociatedCertificate { @Column(length = MAX_MESSAGE_LENGTH) private String componentFailures = Strings.EMPTY; + @Column(length = MAX_MESSAGE_LENGTH) + private String componentFailureMessage = Strings.EMPTY; @Transient private EndorsementCredential endorsementCredential = null; diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/CertificateRequestHandler.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/CertificateRequestHandler.java index 2ca0611c..5eb42b76 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/CertificateRequestHandler.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/CertificateRequestHandler.java @@ -198,6 +198,7 @@ public class CertificateRequestHandler extends AbstractRequestHandler { * @return the {@link AppraisalStatus} of the supply chain validation */ private AppraisalStatus.Status doQuoteValidation(final Device device) { + log.info("Beginning Quote Validation..."); // perform supply chain validation SupplyChainValidationSummary scvs = supplyChainValidationService.validateQuote( device); diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/IdentityClaimHandler.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/IdentityClaimHandler.java index a2d6dfc8..4dc3458c 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/IdentityClaimHandler.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/IdentityClaimHandler.java @@ -124,6 +124,7 @@ public class IdentityClaimHandler extends AbstractRequestHandler { try { validationResult = doSupplyChainValidation(claim, ekPub); } catch (Exception ex) { + log.error(ex.getMessage()); for (StackTraceElement ste : ex.getStackTrace()) { log.error(ste.toString()); } @@ -191,12 +192,15 @@ public class IdentityClaimHandler extends AbstractRequestHandler { // this is to check what is in the platform object and pull // additional information from the DB if information exists if (platformCredentials.size() == 1) { + List tempList = new LinkedList<>(); for (PlatformCredential pc : platformCredentials) { if (pc != null && pc.getPlatformSerial() != null) { - platformCredentials.addAll(certificateRepository + tempList.addAll(certificateRepository .byBoardSerialNumber(pc.getPlatformSerial())); } } + + platformCredentials.addAll(tempList); } // perform supply chain validation SupplyChainValidationSummary summary = supplyChainValidationService.validateSupplyChain( @@ -227,6 +231,9 @@ public class IdentityClaimHandler extends AbstractRequestHandler { log.info("Processing Device Info Report"); // store device and device info report. Device device = this.deviceRepository.findByName(deviceInfoReport.getNetworkInfo().getHostname()); + if (device == null) { + device = new Device(deviceInfoReport); + } device.setDeviceInfo(deviceInfoReport); return this.deviceRepository.save(device); } diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/service/SupplyChainValidationService.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/service/SupplyChainValidationService.java index 55e01b09..1c6e9ec1 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/service/SupplyChainValidationService.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/service/SupplyChainValidationService.java @@ -1,30 +1,259 @@ package hirs.attestationca.persist.service; +import hirs.attestationca.persist.DBManagerException; +import hirs.attestationca.persist.entity.ArchivableEntity; +import hirs.attestationca.persist.entity.manager.CACredentialRepository; +import hirs.attestationca.persist.entity.manager.CertificateRepository; +import hirs.attestationca.persist.entity.manager.ComponentResultRepository; +import hirs.attestationca.persist.entity.manager.PolicyRepository; +import hirs.attestationca.persist.entity.manager.ReferenceDigestValueRepository; +import hirs.attestationca.persist.entity.manager.ReferenceManifestRepository; +import hirs.attestationca.persist.entity.manager.SupplyChainValidationSummaryRepository; import hirs.attestationca.persist.entity.userdefined.Device; +import hirs.attestationca.persist.entity.userdefined.PolicySettings; +import hirs.attestationca.persist.entity.userdefined.SupplyChainValidation; import hirs.attestationca.persist.entity.userdefined.SupplyChainValidationSummary; import hirs.attestationca.persist.entity.userdefined.certificate.EndorsementCredential; import hirs.attestationca.persist.entity.userdefined.certificate.PlatformCredential; +import hirs.attestationca.persist.entity.userdefined.rim.EventLogMeasurements; +import hirs.attestationca.persist.entity.userdefined.rim.SupportReferenceManifest; +import hirs.attestationca.persist.enums.AppraisalStatus; +import hirs.attestationca.persist.validation.PcrValidator; +import hirs.attestationca.persist.validation.SupplyChainCredentialValidator; +import lombok.extern.log4j.Log4j2; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import java.security.KeyStore; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedList; import java.util.List; +import java.util.Map; + +import org.apache.logging.log4j.Level; + +import static hirs.attestationca.persist.enums.AppraisalStatus.Status.FAIL; +import static hirs.attestationca.persist.enums.AppraisalStatus.Status.PASS; + +@Log4j2 +@Service +public class SupplyChainValidationService { + + private CACredentialRepository caCredentialRepository; + private PolicyRepository policyRepository; + private ReferenceManifestRepository referenceManifestRepository; + private ReferenceDigestValueRepository referenceDigestValueRepository; + private ComponentResultRepository componentResultRepository; + private CertificateRepository certificateRepository; + private SupplyChainValidationSummaryRepository supplyChainValidationSummaryRepository; -/** - * Interface defining a component that will perform supply chain validations, which yields a - * {@link SupplyChainValidationSummary}. - */ -public interface SupplyChainValidationService { /** - * The "main" method of supply chain validation. Takes the credentials from an identity - * request and validates the supply chain in accordance to the current supply chain - * policy. + * Constructor. * - * @param ec The endorsement credential from the identity request. - * @param pc The set of platform credentials from the identity request. - * @param device The device to be validated. - * @return True if validation is successful, false otherwise. + * @param caCredentialRepository ca credential repository + * @param policyRepository the policy manager + * @param certificateRepository the cert manager + * @param componentResultRepository the comp result manager + * @param referenceManifestRepository the RIM manager + * @param supplyChainValidationSummaryRepository the summary manager + * @param referenceDigestValueRepository the even manager */ - SupplyChainValidationSummary validateSupplyChain(EndorsementCredential ec, - List pc, - Device device); + @Autowired + @SuppressWarnings("ParameterNumberCheck") + public SupplyChainValidationService( + final CACredentialRepository caCredentialRepository, + final PolicyRepository policyRepository, + final CertificateRepository certificateRepository, + final ComponentResultRepository componentResultRepository, + final ReferenceManifestRepository referenceManifestRepository, + final SupplyChainValidationSummaryRepository supplyChainValidationSummaryRepository, + final ReferenceDigestValueRepository referenceDigestValueRepository) { + this.caCredentialRepository = caCredentialRepository; + this.policyRepository = policyRepository; + this.certificateRepository = certificateRepository; + this.componentResultRepository = componentResultRepository; + this.referenceManifestRepository = referenceManifestRepository; + this.supplyChainValidationSummaryRepository = supplyChainValidationSummaryRepository; + this.referenceDigestValueRepository = referenceDigestValueRepository; + } + + /** + * The "main" method of supply chain validation. Takes the credentials from + * an identity request and validates the supply chain in accordance to the + * current supply chain policy. + * + * @param ec The endorsement credential from the identity request. + * @param pcs The platform credentials from the identity request. + * @param device The device to be validated. + * @return A summary of the validation results. + */ + @SuppressWarnings("methodlength") + public SupplyChainValidationSummary validateSupplyChain(final EndorsementCredential ec, + final List pcs, + final Device device) { + boolean acceptExpiredCerts = getPolicySettings().isExpiredCertificateValidationEnabled(); + PlatformCredential baseCredential = null; + SupplyChainValidation platformScv = null; + SupplyChainValidation basePlatformScv = null; + boolean chkDeltas = false; + String pcErrorMessage = ""; + List validations = new LinkedList<>(); + Map deltaMapping = new HashMap<>(); + SupplyChainValidation.ValidationType platformType = SupplyChainValidation + .ValidationType.PLATFORM_CREDENTIAL; + log.info("Beginning Supply Chain Validation..."); + + log.info("Beginning Endorsement Credential Validation..."); + // Validate the Endorsement Credential + if (getPolicySettings().isEcValidationEnabled()) { + validations.add(ValidationManager.evaluateEndorsementCredentialStatus(ec, this.caCredentialRepository, acceptExpiredCerts)); + // store the device with the credential + if (ec != null) { + ec.setDeviceId(device.getId()); + this.certificateRepository.save(ec); + } + } + + log.info("Beginning Platform Credential Validation..."); + // Validate Platform Credential signatures + if (getPolicySettings().isPcValidationEnabled()) { + // Ensure there are platform credentials to validate + if (pcs == null || pcs.isEmpty()) { + log.error("There were no Platform Credentials to validate."); + pcErrorMessage = "Platform credential(s) missing\n"; + } else { + for (PlatformCredential pc : pcs) { + KeyStore trustedCa = ValidationManager.getCaChain(pc, caCredentialRepository); + platformScv = ValidationManager.evaluatePlatformCredentialStatus( + pc, trustedCa, acceptExpiredCerts); + + if (platformScv.getValidationResult() == AppraisalStatus.Status.FAIL) { + pcErrorMessage = String.format("%s%s%n", pcErrorMessage, + platformScv.getMessage()); + } + // set the base credential + if (pc.isPlatformBase()) { + baseCredential = pc; + basePlatformScv = platformScv; + } else { + chkDeltas = true; + deltaMapping.put(pc, null); + } + pc.setDeviceId(device.getId()); + this.certificateRepository.save(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.isPlatformBase() && (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 { + if (pcs == null) { + validations.add(new SupplyChainValidation(platformType, + AppraisalStatus.Status.FAIL, new ArrayList<>(), pcErrorMessage)); + } else { + validations.add(new SupplyChainValidation(platformType, + AppraisalStatus.Status.FAIL, new ArrayList<>(pcs), pcErrorMessage)); + } + } + } + + log.info("Beginning Platform Attributes Validation..."); + // Validate Platform Credential attributes + if (getPolicySettings().isPcAttributeValidationEnabled() + && pcErrorMessage.isEmpty()) { + // Ensure there are platform credentials to validate + SupplyChainValidation attributeScv = null; + String attrErrorMessage = ""; + List aes = new ArrayList<>(); + // need to check if there are deltas, if not then just verify + // components of the base + if (baseCredential == null) { + validations.add(ValidationManager.buildValidationRecord( + SupplyChainValidation.ValidationType.PLATFORM_CREDENTIAL, + AppraisalStatus.Status.FAIL, + "Base Platform credential missing." + + " Cannot validate attributes", + null, Level.ERROR)); + } else { + if (chkDeltas) { + aes.addAll(basePlatformScv.getCertificatesUsed()); + Iterator it = pcs.iterator(); + while (it.hasNext()) { + PlatformCredential pc = it.next(); + if (pc != null && !pc.isPlatformBase()) { + attributeScv = ValidationManager.evaluateDeltaAttributesStatus( + pc, device.getDeviceInfo(), + baseCredential, deltaMapping, certificateRepository); + if (attributeScv.getValidationResult() == AppraisalStatus.Status.FAIL) { + attrErrorMessage = String.format("%s%s%n", attrErrorMessage, + attributeScv.getMessage()); + } + } + } + } else { + aes.add(baseCredential); + validations.remove(platformScv); + // if there are no deltas, just check base credential + platformScv = ValidationManager.evaluatePCAttributesStatus( + baseCredential, device.getDeviceInfo(), ec, + certificateRepository, componentResultRepository); + validations.add(new SupplyChainValidation( + SupplyChainValidation.ValidationType.PLATFORM_CREDENTIAL, + platformScv.getValidationResult(), aes, platformScv.getMessage())); + } + } + if (!attrErrorMessage.isEmpty()) { + //combine platform and platform attributes + validations.remove(platformScv); + validations.add(new SupplyChainValidation( + SupplyChainValidation.ValidationType.PLATFORM_CREDENTIAL, + attributeScv.getValidationResult(), aes, attributeScv.getMessage())); + } + } + + log.info("Beginning Firmware Validation..."); + if (getPolicySettings().isFirmwareValidationEnabled()) { + // may need to associated with device to pull the correct info + // compare tpm quote with what is pulled from RIM associated file + validations.add(ValidationManager.evaluateFirmwareStatus(device, getPolicySettings(), + referenceManifestRepository, referenceDigestValueRepository, + caCredentialRepository)); + } + + log.info("The validation finished, summarizing..."); + // Generate validation summary, save it, and return it. + SupplyChainValidationSummary summary + = new SupplyChainValidationSummary(device, validations); + try { + supplyChainValidationSummaryRepository.save(summary); + } catch (DBManagerException dbMEx) { + log.error("Failed to save Supply Chain Summary"); + } + + return summary; + } /** * A supplemental method that handles validating just the quote post main validation. @@ -32,11 +261,110 @@ public interface SupplyChainValidationService { * @param device the associated device. * @return True if validation is successful, false otherwise. */ - SupplyChainValidationSummary validateQuote(Device device); + public SupplyChainValidationSummary validateQuote(final Device device) { + SupplyChainValidation quoteScv = null; + SupplyChainValidationSummary summary = null; + Level level = Level.ERROR; + AppraisalStatus fwStatus = new AppraisalStatus(FAIL, + "Unknown exception caught during quote validation."); + SupportReferenceManifest sRim = null; + EventLogMeasurements eventLog = null; + + // check if the policy is enabled + if (getPolicySettings().isFirmwareValidationEnabled()) { + String[] baseline = new String[Integer.SIZE]; + String deviceName = device.getDeviceInfo() + .getNetworkInfo().getHostname(); + + try { + List supportRims = referenceManifestRepository + .getSupportByManufacturerModel( + device.getDeviceInfo().getHardwareInfo().getManufacturer(), + device.getDeviceInfo().getHardwareInfo().getProductName()); + for (SupportReferenceManifest support : supportRims) { + if (support.isBaseSupport()) { + sRim = support; + } + } + eventLog = (EventLogMeasurements) referenceManifestRepository + .findByHexDecHash(sRim.getEventLogHash()); + + if (sRim == null) { + fwStatus = new AppraisalStatus(FAIL, + String.format("Firmware Quote validation failed: " + + "No associated Support RIM file " + + "could be found for %s", + deviceName)); + } else if (eventLog == null) { + fwStatus = new AppraisalStatus(FAIL, + String.format("Firmware Quote validation failed: " + + "No associated Client Log file " + + "could be found for %s", + deviceName)); + } else { + baseline = sRim.getExpectedPCRList(); + String[] storedPcrs = eventLog.getExpectedPCRList(); + PcrValidator pcrValidator = new PcrValidator(baseline); + // grab the quote + byte[] hash = device.getDeviceInfo().getTpmInfo().getTpmQuoteHash(); + if (pcrValidator.validateQuote(hash, storedPcrs, getPolicySettings())) { + level = Level.INFO; + fwStatus = new AppraisalStatus(PASS, + SupplyChainCredentialValidator.FIRMWARE_VALID); + fwStatus.setMessage("Firmware validation of TPM Quote successful."); + } else { + fwStatus.setMessage("Firmware validation of TPM Quote failed." + + "\nPCR hash and Quote hash do not match."); + } + eventLog.setOverallValidationResult(fwStatus.getAppStatus()); + this.referenceManifestRepository.save(eventLog); + } + } catch (Exception ex) { + log.error(ex); + } + + quoteScv = ValidationManager.buildValidationRecord(SupplyChainValidation + .ValidationType.FIRMWARE, + fwStatus.getAppStatus(), fwStatus.getMessage(), eventLog, level); + + // Generate validation summary, save it, and return it. + List validations = new ArrayList<>(); + SupplyChainValidationSummary previous + = this.supplyChainValidationSummaryRepository.findByDevice(deviceName); + for (SupplyChainValidation scv : previous.getValidations()) { + if (scv.getValidationType() != SupplyChainValidation.ValidationType.FIRMWARE) { + validations.add(ValidationManager.buildValidationRecord(scv.getValidationType(), + scv.getValidationResult(), scv.getMessage(), + scv.getCertificatesUsed().get(0), Level.INFO)); + } + } + validations.add(quoteScv); + previous.archive(); + supplyChainValidationSummaryRepository.save(previous); + summary = new SupplyChainValidationSummary(device, validations); + + // try removing the supply chain validation as well and resaving that + try { + supplyChainValidationSummaryRepository.save(summary); + } catch (DBManagerException dbEx) { + log.error("Failed to save Supply Chain Summary", dbEx); + } + } + + return summary; + } /** - * Allows other service access to the policy information. - * @return supply chain policy + * Helper function to get a fresh load of the default policy from the DB. + * + * @return The default Supply Chain Policy */ -// SupplyChainPolicy getPolicy(); + private PolicySettings getPolicySettings() { + PolicySettings defaultSettings = this.policyRepository.findByName("Default"); + + if (defaultSettings == null) { + defaultSettings = new PolicySettings("Default", "Settings are configured for no validation flags set."); + } + return defaultSettings; + } } diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/service/SupplyChainValidationServiceImpl.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/service/SupplyChainValidationServiceImpl.java deleted file mode 100644 index 02c7c7ab..00000000 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/service/SupplyChainValidationServiceImpl.java +++ /dev/null @@ -1,377 +0,0 @@ -package hirs.attestationca.persist.service; - -import hirs.attestationca.persist.entity.ArchivableEntity; -import hirs.attestationca.persist.DBManagerException; -import hirs.attestationca.persist.entity.manager.CACredentialRepository; -import hirs.attestationca.persist.entity.manager.CertificateRepository; -import hirs.attestationca.persist.entity.manager.ComponentResultRepository; -import hirs.attestationca.persist.entity.manager.PolicyRepository; -import hirs.attestationca.persist.entity.manager.ReferenceDigestValueRepository; -import hirs.attestationca.persist.entity.manager.ReferenceManifestRepository; -import hirs.attestationca.persist.entity.manager.SupplyChainValidationSummaryRepository; -import hirs.attestationca.persist.entity.userdefined.Certificate; -import hirs.attestationca.persist.entity.userdefined.Device; -import hirs.attestationca.persist.entity.userdefined.PolicySettings; -import hirs.attestationca.persist.entity.userdefined.SupplyChainValidation; -import hirs.attestationca.persist.entity.userdefined.SupplyChainValidationSummary; -import hirs.attestationca.persist.entity.userdefined.certificate.CertificateAuthorityCredential; -import hirs.attestationca.persist.entity.userdefined.certificate.EndorsementCredential; -import hirs.attestationca.persist.entity.userdefined.certificate.PlatformCredential; -import hirs.attestationca.persist.entity.userdefined.record.TPMMeasurementRecord; -import hirs.attestationca.persist.entity.userdefined.rim.EventLogMeasurements; -import hirs.attestationca.persist.entity.userdefined.rim.SupportReferenceManifest; -import hirs.attestationca.persist.enums.AppraisalStatus; -import hirs.attestationca.persist.validation.CredentialValidator; -import hirs.attestationca.persist.validation.PcrValidator; -import hirs.attestationca.persist.validation.SupplyChainCredentialValidator; -import hirs.utils.BouncyCastleUtils; -import lombok.extern.log4j.Log4j2; -import org.bouncycastle.util.encoders.Hex; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; - -import java.io.IOException; -import java.security.KeyStore; -import java.security.KeyStoreException; -import java.security.NoSuchAlgorithmException; -import java.security.cert.CertificateException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Set; -import org.apache.logging.log4j.Level; - -import static hirs.attestationca.persist.enums.AppraisalStatus.Status.FAIL; -import static hirs.attestationca.persist.enums.AppraisalStatus.Status.PASS; - -@Log4j2 -@Service -public class SupplyChainValidationServiceImpl implements SupplyChainValidationService { - - private CACredentialRepository caCredentialRepository; - private PolicyRepository policyRepository; - private ReferenceManifestRepository referenceManifestRepository; - private ReferenceDigestValueRepository referenceDigestValueRepository; - private ComponentResultRepository componentResultRepository; - private CertificateRepository certificateRepository; - private CredentialValidator supplyChainCredentialValidator; - private SupplyChainValidationSummaryRepository supplyChainValidationSummaryRepository; - - /** - * Constructor to set just the CertificateRepository, so that cert chain validating - * methods can be called from outside classes. - * - * @param certificateRepository the cert repository - */ - public SupplyChainValidationServiceImpl(final CertificateRepository certificateRepository) { - this.certificateRepository = certificateRepository; - } - - /** - * Constructor. - * - * @param caCredentialRepository ca credential repository - * @param policyRepository the policy manager - * @param certificateRepository the cert manager - * @param componentResultRepository the comp result manager - * @param referenceManifestRepository the RIM manager - * @param supplyChainValidationSummaryRepository the summary manager - * @param supplyChainCredentialValidator the credential validator - * @param referenceDigestValueRepository the even manager - */ - @Autowired - @SuppressWarnings("ParameterNumberCheck") - public SupplyChainValidationServiceImpl( - final CACredentialRepository caCredentialRepository, - final PolicyRepository policyRepository, - final CertificateRepository certificateRepository, - final ComponentResultRepository componentResultRepository, - final ReferenceManifestRepository referenceManifestRepository, - final SupplyChainValidationSummaryRepository supplyChainValidationSummaryRepository, - final CredentialValidator supplyChainCredentialValidator, - final ReferenceDigestValueRepository referenceDigestValueRepository) { - this.caCredentialRepository = caCredentialRepository; - this.policyRepository = policyRepository; - this.certificateRepository = certificateRepository; - this.componentResultRepository = componentResultRepository; - this.referenceManifestRepository = referenceManifestRepository; - this.supplyChainValidationSummaryRepository = supplyChainValidationSummaryRepository; - this.supplyChainCredentialValidator = supplyChainCredentialValidator; - this.referenceDigestValueRepository = referenceDigestValueRepository; - } - - @Override - public SupplyChainValidationSummary validateSupplyChain(final EndorsementCredential ec, - final List pc, - final Device device) { - return null; - } - - /** - * A supplemental method that handles validating just the quote post main validation. - * - * @param device the associated device. - * @return True if validation is successful, false otherwise. - */ - @Override - public SupplyChainValidationSummary validateQuote(final Device device) { - SupplyChainValidation quoteScv = null; - SupplyChainValidationSummary summary = null; - Level level = Level.ERROR; - AppraisalStatus fwStatus = new AppraisalStatus(FAIL, - "Unknown exception caught during quote validation."); - SupportReferenceManifest sRim = null; - EventLogMeasurements eventLog = null; - - // check if the policy is enabled - if (getPolicySettings().isFirmwareValidationEnabled()) { - String[] baseline = new String[Integer.SIZE]; - String deviceName = device.getDeviceInfo() - .getNetworkInfo().getHostname(); - - try { - List supportRims = referenceManifestRepository.getSupportByManufacturerModel( - device.getDeviceInfo().getHardwareInfo().getManufacturer(), - device.getDeviceInfo().getHardwareInfo().getProductName()); - for (SupportReferenceManifest support : supportRims) { - if (support.isBaseSupport()) { - sRim = support; - } - } - eventLog = (EventLogMeasurements) referenceManifestRepository - .findByHexDecHash(sRim.getEventLogHash()); - - if (sRim == null) { - fwStatus = new AppraisalStatus(FAIL, - String.format("Firmware Quote validation failed: " - + "No associated Support RIM file " - + "could be found for %s", - deviceName)); - } else if (eventLog == null) { - fwStatus = new AppraisalStatus(FAIL, - String.format("Firmware Quote validation failed: " - + "No associated Client Log file " - + "could be found for %s", - deviceName)); - } else { - baseline = sRim.getExpectedPCRList(); - String[] storedPcrs = eventLog.getExpectedPCRList(); - PcrValidator pcrValidator = new PcrValidator(baseline); - // grab the quote - byte[] hash = device.getDeviceInfo().getTpmInfo().getTpmQuoteHash(); - if (pcrValidator.validateQuote(hash, storedPcrs, getPolicySettings())) { - level = Level.INFO; - fwStatus = new AppraisalStatus(PASS, - SupplyChainCredentialValidator.FIRMWARE_VALID); - fwStatus.setMessage("Firmware validation of TPM Quote successful."); - } else { - fwStatus.setMessage("Firmware validation of TPM Quote failed." - + "\nPCR hash and Quote hash do not match."); - } - eventLog.setOverallValidationResult(fwStatus.getAppStatus()); - this.referenceManifestRepository.save(eventLog); - } - } catch (Exception ex) { - log.error(ex); - } - - quoteScv = buildValidationRecord(SupplyChainValidation - .ValidationType.FIRMWARE, - fwStatus.getAppStatus(), fwStatus.getMessage(), eventLog, level); - - // Generate validation summary, save it, and return it. - List validations = new ArrayList<>(); - SupplyChainValidationSummary previous - = this.supplyChainValidationSummaryRepository.findByDevice(deviceName); - for (SupplyChainValidation scv : previous.getValidations()) { - if (scv.getValidationType() != SupplyChainValidation.ValidationType.FIRMWARE) { - validations.add(buildValidationRecord(scv.getValidationType(), - scv.getValidationResult(), scv.getMessage(), - scv.getCertificatesUsed().get(0), Level.INFO)); - } - } - validations.add(quoteScv); - previous.archive(); - supplyChainValidationSummaryRepository.save(previous); - summary = new SupplyChainValidationSummary(device, validations); - - // try removing the supply chain validation as well and resaving that - try { - supplyChainValidationSummaryRepository.save(summary); - } catch (DBManagerException dbEx) { - log.error("Failed to save Supply Chain Summary", dbEx); - } - } - - return summary; - } - - /** - * Creates a supply chain validation record and logs the validation message - * at the specified log level. - * - * @param validationType the type of validation - * @param result the appraisal status - * @param message the validation message to include in the summary and log - * @param archivableEntity the archivableEntity associated with the - * validation - * @param logLevel the log level - * @return a SupplyChainValidation - */ - private SupplyChainValidation buildValidationRecord( - final SupplyChainValidation.ValidationType validationType, - final AppraisalStatus.Status result, final String message, - final ArchivableEntity archivableEntity, final Level logLevel) { - List aeList = new ArrayList<>(); - if (archivableEntity != null) { - aeList.add(archivableEntity); - } - - log.log(logLevel, message); - return new SupplyChainValidation(validationType, result, aeList, message); - } - - /** - * This method is used to retrieve the entire CA chain (up to a trusted - * self-signed certificate) for the given certificate. This method will look - * up CA certificates that have a matching issuer organization as the given - * certificate, and will perform that operation recursively until all - * certificates for all relevant organizations have been retrieved. For that - * reason, the returned set of certificates may be larger than the the - * single trust chain for the queried certificate, but is guaranteed to - * include the trust chain if it exists in this class' CertificateManager. - * Returns the certificate authority credentials in a KeyStore. - * - * @param credential the credential whose CA chain should be retrieved - * @return A keystore containing all relevant CA credentials to the given - * certificate's organization or null if the keystore can't be assembled - */ - public KeyStore getCaChain(final Certificate credential) { - KeyStore caKeyStore = null; - try { - caKeyStore = caCertSetToKeystore(getCaChainRec(credential, Collections.emptySet())); - } catch (KeyStoreException | IOException e) { - log.error("Unable to assemble CA keystore", e); - } - return caKeyStore; - } - - /** - * This is a recursive method which is used to retrieve the entire CA chain - * (up to a trusted self-signed certificate) for the given certificate. This - * method will look up CA certificates that have a matching issuer - * organization as the given certificate, and will perform that operation - * recursively until all certificates for all relevant organizations have - * been retrieved. For that reason, the returned set of certificates may be - * larger than the the single trust chain for the queried certificate, but - * is guaranteed to include the trust chain if it exists in this class' - * CertificateManager. - *

- * Implementation notes: 1. Queries for CA certs with a subject org matching - * the given (argument's) issuer org 2. Add that org to - * queriedOrganizations, so we don't search for that organization again 3. - * For each returned CA cert, add that cert to the result set, and recurse - * with that as the argument (to go up the chain), if and only if we haven't - * 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 previouslyQueriedSubjects a list of organizations to refrain - * from querying - * @return a Set containing all relevant CA credentials to the given - * certificate's organization - */ - private Set getCaChainRec( - final Certificate credential, - final Set previouslyQueriedSubjects) { - CertificateAuthorityCredential skiCA = null; - List certAuthsWithMatchingIssuer = new LinkedList<>(); - if (credential.getAuthorityKeyIdentifier() != null - && !credential.getAuthorityKeyIdentifier().isEmpty()) { - byte[] bytes = Hex.decode(credential.getAuthorityKeyIdentifier()); - // CYRUS is SKI unique? - skiCA = caCredentialRepository.findBySubjectKeyIdentifier(bytes); - } - - if (skiCA == null) { - if (credential.getIssuerSorted() == null - || credential.getIssuerSorted().isEmpty()) { - certAuthsWithMatchingIssuer = caCredentialRepository.findBySubject(credential.getIssuer()); - } else { - //Get certificates by subject organization - certAuthsWithMatchingIssuer = caCredentialRepository.findBySubjectSorted(credential.getIssuerSorted()); - } - } else { - certAuthsWithMatchingIssuer.add(skiCA); - } - Set queriedOrganizations = new HashSet<>(previouslyQueriedSubjects); - queriedOrganizations.add(credential.getIssuer()); - - HashSet caCreds = new HashSet<>(); - for (CertificateAuthorityCredential cred : certAuthsWithMatchingIssuer) { - caCreds.add(cred); - if (!BouncyCastleUtils.x500NameCompare(cred.getIssuer(), - cred.getSubject())) { - caCreds.addAll(getCaChainRec(cred, queriedOrganizations)); - } - } - return caCreds; - } - - private KeyStore caCertSetToKeystore(final Set certs) - throws KeyStoreException, IOException { - KeyStore keyStore = KeyStore.getInstance("JKS"); - try { - keyStore.load(null, "".toCharArray()); - for (Certificate cert : certs) { - keyStore.setCertificateEntry(cert.getId().toString(), cert.getX509Certificate()); - } - } catch (IOException | CertificateException | NoSuchAlgorithmException e) { - throw new IOException("Could not create and populate keystore", e); - } - - return keyStore; - } - - private String[] buildStoredPcrs(final String pcrContent, final int algorithmLength) { - // we have a full set of PCR values - String[] pcrSet = pcrContent.split("\\n"); - String[] storedPcrs = new String[TPMMeasurementRecord.MAX_PCR_ID + 1]; - - // we need to scroll through the entire list until we find - // a matching hash length - int offset = 1; - - for (int i = 0; i < pcrSet.length; i++) { - if (pcrSet[i].contains("sha")) { - // entered a new set, check size - if (pcrSet[i + offset].split(":")[1].trim().length() - == algorithmLength) { - // found the matching set - for (int j = 0; j <= TPMMeasurementRecord.MAX_PCR_ID; j++) { - storedPcrs[j] = pcrSet[++i].split(":")[1].trim(); - } - break; - } - } - } - - return storedPcrs; - } - - /** - * Helper function to get a fresh load of the default policy from the DB. - * - * @return The default Supply Chain Policy - */ - private PolicySettings getPolicySettings() { - PolicySettings defaultSettings = this.policyRepository.findByName("Default"); - - if (defaultSettings == null) { - defaultSettings = new PolicySettings("Default", "Settings are configured for no validation flags set."); - } - return defaultSettings; - } -} diff --git a/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/utils/PciIds.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/util/PciIds.java similarity index 99% rename from HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/utils/PciIds.java rename to HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/util/PciIds.java index 3623e922..e17fd1e8 100644 --- a/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/utils/PciIds.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/util/PciIds.java @@ -1,4 +1,4 @@ -package hirs.attestationca.portal.page.utils; +package hirs.attestationca.persist.util; import com.github.marandus.pciid.model.Device; import com.github.marandus.pciid.model.Vendor; diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/validation/CredentialValidator.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/validation/CredentialValidator.java index 7e4638bf..2084cfe9 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/validation/CredentialValidator.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/validation/CredentialValidator.java @@ -1,19 +1,86 @@ package hirs.attestationca.persist.validation; -import hirs.attestationca.persist.entity.userdefined.SupplyChainValidation; import hirs.attestationca.persist.entity.userdefined.certificate.EndorsementCredential; import hirs.attestationca.persist.entity.userdefined.certificate.PlatformCredential; import hirs.attestationca.persist.entity.userdefined.report.DeviceInfoReport; import hirs.attestationca.persist.enums.AppraisalStatus; +import lombok.extern.log4j.Log4j2; +import org.bouncycastle.cert.X509AttributeCertificateHolder; +import java.io.IOException; import java.security.KeyStore; -import java.util.Map; +import java.security.KeyStoreException; +import java.security.cert.CertificateExpiredException; +import java.security.cert.CertificateNotYetValidException; +import java.security.cert.X509Certificate; +import java.util.Date; + +import static hirs.attestationca.persist.enums.AppraisalStatus.Status.ERROR; +import static hirs.attestationca.persist.enums.AppraisalStatus.Status.FAIL; +import static hirs.attestationca.persist.enums.AppraisalStatus.Status.PASS; + +@Log4j2 +public class CredentialValidator extends SupplyChainCredentialValidator { + + /** + * Checks if the endorsement credential is valid. + * + * @param ec the endorsement credential to verify. + * @param trustStore trust store holding trusted trusted certificates. + * @param acceptExpired whether or not to accept expired and not yet valid certificates + * as valid. + * @return the result of the validation. + */ + public static AppraisalStatus validateEndorsementCredential(final EndorsementCredential ec, + final KeyStore trustStore, + final boolean acceptExpired) { + final String baseErrorMessage = "Can't validate endorsement credential attributes without "; + String message; + if (ec == null) { + message = baseErrorMessage + "an endorsement credential"; + log.error(message); + return new AppraisalStatus(FAIL, message); + } + if (trustStore == null) { + message = baseErrorMessage + "a trust store"; + log.error(message); + return new AppraisalStatus(FAIL, message); + } + + try { + X509Certificate verifiableCert = ec.getX509Certificate(); + + // check validity period, currently acceptExpired will also accept not yet + // valid certificates + if (!acceptExpired) { + verifiableCert.checkValidity(); + } + + if (verifyCertificate(verifiableCert, trustStore)) { + return new AppraisalStatus(PASS, ENDORSEMENT_VALID); + } else { + return new AppraisalStatus(FAIL, "Endorsement credential does not have a valid " + + "signature chain in the trust store"); + } + } catch (IOException e) { + message = "Couldn't retrieve X509 certificate from endorsement credential"; + log.error(message, e); + return new AppraisalStatus(ERROR, message + " " + e.getMessage()); + } catch (SupplyChainValidatorException e) { + message = "An error occurred indicating the credential is not valid"; + log.warn(message, e); + return new AppraisalStatus(ERROR, message + " " + e.getMessage()); + } catch (CertificateExpiredException e) { + message = "The endorsement credential is expired"; + log.warn(message, e); + return new AppraisalStatus(FAIL, message + " " + e.getMessage()); + } catch (CertificateNotYetValidException e) { + message = "The endorsement credential is not yet valid"; + log.warn(message, e); + return new AppraisalStatus(FAIL, message + " " + e.getMessage()); + } + } -/** - * A class used to support supply chain validation by performing the actual - * validation of credentials. - */ -public interface CredentialValidator { /** * Checks if the platform credential is valid. * @@ -22,47 +89,113 @@ public interface CredentialValidator { * @param acceptExpired whether or not to accept expired certificates as valid. * @return The result of the validation. */ - AppraisalStatus validatePlatformCredential(PlatformCredential pc, - KeyStore trustStore, - boolean acceptExpired); + public static AppraisalStatus validatePlatformCredential(final PlatformCredential pc, + final KeyStore trustStore, + final boolean acceptExpired) { + final String baseErrorMessage = "Can't validate platform credential without "; + String message; + String certVerifyMsg; + if (pc == null) { + message = baseErrorMessage + "a platform credential\n"; + log.error(message); + return new AppraisalStatus(FAIL, message); + } + try { + if (trustStore == null || trustStore.size() == 0) { + message = baseErrorMessage + "an Issuer Cert in the Trust Store\n"; + log.error(message); + return new AppraisalStatus(FAIL, message); + } + } catch (KeyStoreException e) { + message = baseErrorMessage + "an initialized trust store"; + log.error(message); + return new AppraisalStatus(FAIL, message); + } + + X509AttributeCertificateHolder attributeCert = null; + try { + attributeCert = pc.getX509AttributeCertificateHolder(); + } catch (IOException e) { + message = "Could not retrieve X509 Attribute certificate"; + log.error(message, e); + return new AppraisalStatus(FAIL, message + " " + e.getMessage()); + } + + // check validity period, currently acceptExpired will also accept not yet + // valid certificates + if (!acceptExpired && !pc.isValidOn(new Date())) { + message = "Platform credential has expired"; + // if not valid at the current time + log.warn(message); + return new AppraisalStatus(FAIL, message); + } + + // verify cert against truststore + try { + certVerifyMsg = verifyCertificate(attributeCert, trustStore); + if (certVerifyMsg.isEmpty()) { + message = PLATFORM_VALID; + log.info(message); + return new AppraisalStatus(PASS, message); + } else { + message = String.format("Platform credential failed verification%n%s", + certVerifyMsg); + log.error(message); + return new AppraisalStatus(FAIL, message); + } + } catch (SupplyChainValidatorException scvEx) { + message = "An error occurred indicating the credential is not valid"; + log.warn(message, scvEx); + return new AppraisalStatus(FAIL, message + " " + scvEx.getMessage()); + } + } /** * Checks if the platform credential's attributes are valid. - * @param pc The platform credential to verify. - * @param deviceInfoReport Report containing the serial numbers of the platform to be validated. - * @param ec The endorsement credential supplied from the same identity request as - * the platform credential. - * @return The result of the validation. - */ - AppraisalStatus validatePlatformCredentialAttributes(PlatformCredential pc, - DeviceInfoReport deviceInfoReport, - EndorsementCredential ec); - - /** - * Checks if the delta credential's attributes are valid. - * @param delta the delta credential to verify + * @param platformCredential The platform credential to verify. * @param deviceInfoReport The device info report containing * serial number of the platform to be validated. - * @param base the base credential from the same identity request - * as the delta credential. - * @param deltaMapping delta certificates associated with the - * delta supply validation. - * @return the result of the validation. + * @param endorsementCredential The endorsement credential supplied from the same + * identity request as the platform credential. + * @return The result of the validation. */ - AppraisalStatus validateDeltaPlatformCredentialAttributes(PlatformCredential delta, - DeviceInfoReport deviceInfoReport, - PlatformCredential base, - Map deltaMapping); - /** - * Checks if the endorsement credential is valid. - * - * @param ec the endorsement credential to verify. - * @param trustStore trust store holding trusted trusted certificates. - * @param acceptExpired whether or not to accept expired certificates as valid. - * @return the result of the validation. - */ - AppraisalStatus validateEndorsementCredential(EndorsementCredential ec, - KeyStore trustStore, - boolean acceptExpired); -} + public static AppraisalStatus validatePlatformCredentialAttributes( + final PlatformCredential platformCredential, + final DeviceInfoReport deviceInfoReport, + final EndorsementCredential endorsementCredential) { + final String baseErrorMessage = "Can't validate platform credential attributes without "; + String message; + if (platformCredential == null) { + message = baseErrorMessage + "a platform credential"; + log.error(message); + return new AppraisalStatus(FAIL, message); + } + if (deviceInfoReport == null) { + message = baseErrorMessage + "a device info report"; + log.error(message); + return new AppraisalStatus(FAIL, message); + } + if (endorsementCredential == null) { + message = baseErrorMessage + "an endorsement credential"; + log.error(message); + return new AppraisalStatus(FAIL, message); + } + + // Quick, early check if the platform credential references the endorsement credential + if (!endorsementCredential.getSerialNumber() + .equals(platformCredential.getHolderSerialNumber())) { + message = "Platform Credential holder serial number does not match " + + "the Endorsement Credential's serial number"; + log.error(message); + return new AppraisalStatus(FAIL, message); + } + + String credentialType = platformCredential.getCredentialType(); + if (PlatformCredential.CERTIFICATE_TYPE_2_0.equals(credentialType)) { + return CertificateAttributeScvValidator.validatePlatformCredentialAttributesV2p0( + platformCredential, deviceInfoReport); + } + return CertificateAttributeScvValidator.validatePlatformCredentialAttributesV1p2( + platformCredential, deviceInfoReport); + } +} \ No newline at end of file diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/validation/PcrValidator.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/validation/PcrValidator.java index 9e8b738c..568ebe72 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/validation/PcrValidator.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/validation/PcrValidator.java @@ -226,4 +226,30 @@ public class PcrValidator { return validated; } + + public static String[] buildStoredPcrs(final String pcrContent, final int algorithmLength) { + // we have a full set of PCR values + String[] pcrSet = pcrContent.split("\\n"); + String[] storedPcrs = new String[TPMMeasurementRecord.MAX_PCR_ID + 1]; + + // we need to scroll through the entire list until we find + // a matching hash length + int offset = 1; + + for (int i = 0; i < pcrSet.length; i++) { + if (pcrSet[i].contains("sha")) { + // entered a new set, check size + if (pcrSet[i + offset].split(":")[1].trim().length() + == algorithmLength) { + // found the matching set + for (int j = 0; j <= TPMMeasurementRecord.MAX_PCR_ID; j++) { + storedPcrs[j] = pcrSet[++i].split(":")[1].trim(); + } + break; + } + } + } + + return storedPcrs; + } } diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/validation/SupplyChainCredentialValidator.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/validation/SupplyChainCredentialValidator.java index 70aaec2c..bcdc9d92 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/validation/SupplyChainCredentialValidator.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/validation/SupplyChainCredentialValidator.java @@ -1,23 +1,50 @@ package hirs.attestationca.persist.validation; -import hirs.attestationca.persist.entity.userdefined.SupplyChainValidation; +import com.fasterxml.jackson.core.JsonFactory; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; import hirs.attestationca.persist.entity.userdefined.certificate.ComponentResult; -import hirs.attestationca.persist.entity.userdefined.certificate.EndorsementCredential; -import hirs.attestationca.persist.entity.userdefined.certificate.PlatformCredential; -import hirs.attestationca.persist.entity.userdefined.report.DeviceInfoReport; -import hirs.attestationca.persist.enums.AppraisalStatus; +import hirs.attestationca.persist.entity.userdefined.certificate.attributes.ComponentIdentifier; +import hirs.attestationca.persist.entity.userdefined.certificate.attributes.V2.ComponentIdentifierV2; +import hirs.attestationca.persist.entity.userdefined.info.ComponentInfo; import lombok.NoArgsConstructor; import lombok.extern.log4j.Log4j2; +import org.apache.commons.lang3.StringUtils; +import org.apache.logging.log4j.util.Strings; +import org.bouncycastle.asn1.DERUTF8String; +import org.bouncycastle.asn1.x500.X500Name; +import org.bouncycastle.cert.CertException; +import org.bouncycastle.cert.X509AttributeCertificateHolder; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.operator.ContentVerifierProvider; +import org.bouncycastle.operator.OperatorCreationException; +import org.bouncycastle.operator.jcajce.JcaContentVerifierProviderBuilder; +import javax.security.auth.x500.X500Principal; +import java.io.IOException; +import java.security.InvalidKeyException; import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.PublicKey; +import java.security.Security; +import java.security.SignatureException; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.HashSet; +import java.util.Iterator; import java.util.LinkedList; import java.util.List; -import java.util.Map; +import java.util.Set; @Log4j2 @NoArgsConstructor -public class SupplyChainCredentialValidator implements CredentialValidator { +public class SupplyChainCredentialValidator { + public static final int NUC_VARIABLE_BIT = 159; /** * AppraisalStatus message for a valid endorsement credential appraisal. */ @@ -39,34 +66,447 @@ public class SupplyChainCredentialValidator implements CredentialValidator { */ public static final String FIRMWARE_VALID = "Firmware validated"; - private static List componentResultList = new LinkedList<>(); + /** + * Ensure that BouncyCastle is configured as a javax.security.Security provider, as this + * class expects it to be available. + */ + static { + Security.addProvider(new BouncyCastleProvider()); + } - @Override - public AppraisalStatus validatePlatformCredential(final PlatformCredential pc, - final KeyStore trustStore, - final boolean acceptExpired) { + /** + * Attempts to check if the certificate is validated by certificates in a cert chain. The cert + * chain is expected to be stored in a non-ordered KeyStore (trust store). If the signing + * certificate for the target cert is found, but it is an intermediate cert, the validation will + * continue to try to find the signing cert of the intermediate cert. It will continue searching + * until it follows the chain up to a root (self-signed) cert. + * + * @param cert + * certificate to validate + * @param trustStore + * trust store holding trusted root certificates and intermediate certificates + * @return the certificate chain if validation is successful + * @throws SupplyChainValidatorException + * if the verification is not successful + */ + public static String verifyCertificate(final X509AttributeCertificateHolder cert, + final KeyStore trustStore) throws SupplyChainValidatorException { + try { + if (cert == null || trustStore == null) { + throw new SupplyChainValidatorException("Certificate or trust store is null"); + } else if (trustStore.size() == 0) { + throw new SupplyChainValidatorException("Truststore is empty"); + } + } catch (KeyStoreException e) { + log.error("Error accessing trust store: " + e.getMessage()); + } + + try { + Set trustedCerts = new HashSet<>(); + + Enumeration alias = trustStore.aliases(); + + while (alias.hasMoreElements()) { + trustedCerts.add((X509Certificate) trustStore.getCertificate(alias.nextElement())); + } + + String certChainValidated = validateCertChain(cert, trustedCerts); + if (!certChainValidated.isEmpty()) { + log.error("Cert chain could not be validated"); + } + return certChainValidated; + } catch (KeyStoreException e) { + throw new SupplyChainValidatorException("Error with the trust store", e); + } + } + + /** + * Attempts to check if the certificate is validated by certificates in a cert chain. The cert + * chain is expected to be stored in a non-ordered KeyStore (trust store). If the signing + * certificate for the target cert is found, but it is an intermediate cert, the validation will + * continue to try to find the signing cert of the intermediate cert. It will continue searching + * until it follows the chain up to a root (self-signed) cert. + * + * @param cert + * certificate to validate + * @param trustStore + * trust store holding trusted root certificates and intermediate certificates + * @return the certificate chain if validation is successful + * @throws SupplyChainValidatorException + * if the verification is not successful + */ + public static boolean verifyCertificate(final X509Certificate cert, + final KeyStore trustStore) throws SupplyChainValidatorException { + try { + if (cert == null || trustStore == null) { + throw new SupplyChainValidatorException("Certificate or trust store is null"); + } else if (trustStore.size() == 0) { + throw new SupplyChainValidatorException("Truststore is empty"); + } + } catch (KeyStoreException e) { + log.error("Error accessing trust store: " + e.getMessage()); + } + + try { + Set trustedCerts = new HashSet<>(); + Enumeration alias = trustStore.aliases(); + + while (alias.hasMoreElements()) { + trustedCerts.add((X509Certificate) trustStore.getCertificate(alias.nextElement())); + } + + return validateCertChain(cert, trustedCerts).isEmpty(); + } catch (KeyStoreException e) { + log.error("Error accessing keystore", e); + throw new SupplyChainValidatorException("Error with the trust store", e); + } + } + + /** + * Attempts to check if an attribute certificate is validated by certificates in a cert chain. + * The cert chain is represented as a Set of X509Certificates. If the signing certificate for + * the target cert is found, but it is an intermediate cert, the validation will continue to try + * to find the signing cert of the intermediate cert. It will continue searching until it + * follows the chain up to a root (self-signed) cert. + * + * @param cert + * certificate to validate + * @param additionalCerts + * Set of certs to validate against + * @return String status of the cert chain validation - + * blank if successful, error message otherwise + * @throws SupplyChainValidatorException tried to validate using null certificates + */ + public static String validateCertChain(final X509AttributeCertificateHolder cert, + final Set additionalCerts) + throws SupplyChainValidatorException { + if (cert == null || additionalCerts == null) { + throw new SupplyChainValidatorException( + "Certificate or validation certificates are null"); + } + final String intCAError = "Intermediate signing cert found, check for CA cert"; + String foundRootOfCertChain = ""; + X509Certificate nextInChain = null; + + do { + for (X509Certificate trustedCert : additionalCerts) { + boolean issuerMatchesSubject = false; + boolean signatureMatchesPublicKey = false; + if (nextInChain != null) { + issuerMatchesSubject = issuerMatchesSubjectDN(nextInChain, trustedCert); + signatureMatchesPublicKey = signatureMatchesPublicKey(nextInChain, + trustedCert); + } else { + issuerMatchesSubject = issuerMatchesSubjectDN(cert, trustedCert); + signatureMatchesPublicKey = signatureMatchesPublicKey(cert, trustedCert); + } + + if (issuerMatchesSubject && signatureMatchesPublicKey) { + if (isSelfSigned(trustedCert)) { + log.info("CA Root found."); + return ""; + } else { + foundRootOfCertChain = intCAError; + nextInChain = trustedCert; + break; + } + } else { + if (!issuerMatchesSubject) { + foundRootOfCertChain = "Issuer DN does not match Subject DN"; + } + if (!signatureMatchesPublicKey) { + foundRootOfCertChain = "Certificate signature failed to verify"; + } + } + } + } while (foundRootOfCertChain.equals(intCAError)); + + log.error(foundRootOfCertChain); + return foundRootOfCertChain; + } + + /** + * Attempts to check if a public-key certificate is validated by certificates in a cert chain. + * The cert chain is represented as a Set of X509Certificates. If the signing certificate for + * the target cert is found, but it is an intermediate cert, the validation will continue to try + * to find the signing cert of the intermediate cert. It will continue searching until it + * follows the chain up to a root (self-signed) cert. + * + * @param cert + * certificate to validate + * @param additionalCerts + * Set of certs to validate against + * @return String status of the cert chain validation - + * blank if successful, error message otherwise + * @throws SupplyChainValidatorException tried to validate using null certificates + */ + public static String validateCertChain(final X509Certificate cert, + final Set additionalCerts) throws SupplyChainValidatorException { + if (cert == null || additionalCerts == null) { + throw new SupplyChainValidatorException( + "Certificate or validation certificates are null"); + } + final String intCAError = "Intermediate signing cert found, check for CA cert"; + String foundRootOfCertChain = ""; + X509Certificate startOfChain = cert; + + do { + for (X509Certificate trustedCert : additionalCerts) { + boolean issuerMatchesSubject = issuerMatchesSubjectDN(startOfChain, trustedCert); + boolean signatureMatchesPublicKey = signatureMatchesPublicKey(startOfChain, + trustedCert); + if (issuerMatchesSubject && signatureMatchesPublicKey) { + if (isSelfSigned(trustedCert)) { + log.info("CA Root found."); + return ""; + } else { + foundRootOfCertChain = intCAError; + startOfChain = trustedCert; + break; + } + } else { + if (!issuerMatchesSubject) { + foundRootOfCertChain = "Issuer DN does not match Subject DN"; + } + if (!signatureMatchesPublicKey) { + foundRootOfCertChain = "Certificate signature failed to verify"; + } + } + } + } while (foundRootOfCertChain.equals(intCAError)); + + log.warn(foundRootOfCertChain); + return foundRootOfCertChain; + } + + /** + * 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 java.io.IOException if something goes wrong parsing the JSON + */ + public static List getComponentInfoFromPaccorOutput(final String paccorOutput) + throws IOException { + List componentInfoList = new ArrayList<>(); + + if (StringUtils.isNotEmpty(paccorOutput)) { + ObjectMapper objectMapper = new ObjectMapper(new JsonFactory()); + JsonNode rootNode = objectMapper.readTree(paccorOutput); + Iterator jsonComponentNodes + = rootNode.findValue("COMPONENTS").elements(); + while (jsonComponentNodes.hasNext()) { + JsonNode next = jsonComponentNodes.next(); + componentInfoList.add(new ComponentInfo( + getJSONNodeValueAsText(next, "MANUFACTURER"), + getJSONNodeValueAsText(next, "MODEL"), + getJSONNodeValueAsText(next, "SERIAL"), + getJSONNodeValueAsText(next, "REVISION"))); + } + } + + 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 getV2PaccorOutput( + final String paccorOutput) throws IOException { + List 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 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 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(); + } return null; } - @Override - public AppraisalStatus validatePlatformCredentialAttributes(final PlatformCredential pc, - final DeviceInfoReport deviceInfoReport, - final EndorsementCredential ec) { - return null; + /** + * Checks if the issuer info of an attribute cert matches the supposed signing cert's + * distinguished name. + * + * @param cert + * the attribute certificate with the signature to validate + * @param signingCert + * the certificate with the public key to validate + * @return boolean indicating if the names + * @throws SupplyChainValidatorException tried to validate using null certificates + */ + public static boolean issuerMatchesSubjectDN(final X509AttributeCertificateHolder cert, + final X509Certificate signingCert) throws SupplyChainValidatorException { + if (cert == null || signingCert == null) { + throw new SupplyChainValidatorException("Certificate or signing certificate is null"); + } + String signingCertSubjectDN = signingCert.getSubjectX500Principal().getName(); + X500Name namedSubjectDN = new X500Name(signingCertSubjectDN); + + X500Name issuerDN = cert.getIssuer().getNames()[0]; + + // equality check ignore DN component ordering + return issuerDN.equals(namedSubjectDN); } - @Override - public AppraisalStatus validateDeltaPlatformCredentialAttributes(final PlatformCredential delta, - final DeviceInfoReport deviceInfoReport, - final PlatformCredential base, - final Map deltaMapping) { - return null; + /** + * Checks if the issuer info of a public-key cert matches the supposed signing cert's + * distinguished name. + * + * @param cert + * the public-key certificate with the signature to validate + * @param signingCert + * the certificate with the public key to validate + * @return boolean indicating if the names + * @throws SupplyChainValidatorException tried to validate using null certificates + */ + public static boolean issuerMatchesSubjectDN(final X509Certificate cert, + final X509Certificate signingCert) throws SupplyChainValidatorException { + if (cert == null || signingCert == null) { + throw new SupplyChainValidatorException("Certificate or signing certificate is null"); + } + String signingCertSubjectDN = signingCert.getSubjectX500Principal(). + getName(X500Principal.RFC1779); + X500Name namedSubjectDN = new X500Name(signingCertSubjectDN); + + String certIssuerDN = cert.getIssuerX500Principal().getName(); + X500Name namedIssuerDN = new X500Name(certIssuerDN); + + // equality check ignore DN component ordering + return namedIssuerDN.equals(namedSubjectDN); } - @Override - public AppraisalStatus validateEndorsementCredential(final EndorsementCredential ec, - final KeyStore trustStore, - final boolean acceptExpired) { - return null; + /** + * Checks if the signature of an attribute cert is validated against the signing cert's public + * key. + * + * @param cert + * the public-key certificate with the signature to validate + * @param signingCert + * the certificate with the public key to validate + * @return boolean indicating if the validation passed + * @throws SupplyChainValidatorException tried to validate using null certificates + */ + public static boolean signatureMatchesPublicKey(final X509Certificate cert, + final X509Certificate signingCert) throws SupplyChainValidatorException { + if (cert == null || signingCert == null) { + throw new SupplyChainValidatorException("Certificate or signing certificate is null"); + } + try { + cert.verify(signingCert.getPublicKey(), BouncyCastleProvider.PROVIDER_NAME); + return true; + } catch (InvalidKeyException e) { + log.info("Incorrect key given to validate this cert's signature"); + } catch (CertificateException e) { + log.info("Encoding error while validating this cert's signature"); + } catch (NoSuchAlgorithmException e) { + log.info("Unsupported signature algorithm found during validation"); + } catch (NoSuchProviderException e) { + log.info("Incorrect provider for cert signature validation"); + } catch (SignatureException e) { + log.info(String.format("%s.verify(%s)", cert.getSubjectX500Principal(), + signingCert.getSubjectX500Principal())); + } + return false; + + } + + /** + * Checks if the signature of a public-key cert is validated against the signing cert's public + * key. + * + * @param cert + * the attribute certificate with the signature to validate + * @param signingCert + * the certificate with the public key to validate + * @return boolean indicating if the validation passed + * @throws SupplyChainValidatorException tried to validate using null certificates + */ + public static boolean signatureMatchesPublicKey(final X509AttributeCertificateHolder cert, + final X509Certificate signingCert) throws SupplyChainValidatorException { + if (signingCert == null) { + throw new SupplyChainValidatorException("Signing certificate is null"); + } + return signatureMatchesPublicKey(cert, signingCert.getPublicKey()); + } + + /** + * Checks if an X509 Attribute Certificate is valid directly against a public key. + * + * @param cert + * the attribute certificate with the signature to validate + * @param signingKey + * the key to use to check the attribute cert + * @return boolean indicating if the validation passed + * @throws SupplyChainValidatorException tried to validate using null certificates + */ + public static boolean signatureMatchesPublicKey(final X509AttributeCertificateHolder cert, + final PublicKey signingKey) throws SupplyChainValidatorException { + if (cert == null || signingKey == null) { + throw new SupplyChainValidatorException("Certificate or signing certificate is null"); + } + ContentVerifierProvider contentVerifierProvider; + try { + contentVerifierProvider = + new JcaContentVerifierProviderBuilder().setProvider("BC").build(signingKey); + return cert.isSignatureValid(contentVerifierProvider); + } catch (OperatorCreationException | CertException e) { + log.info("Exception thrown while verifying certificate", e); + log.info(String.format("%s.isSignatureValid(%s)", cert.getSerialNumber(), + signingKey.getFormat())); + return false; + } + } + + /** + * Checks whether given X.509 public-key certificate is self-signed. If the cert can be + * verified using its own public key, that means it was self-signed. + * + * @param cert + * X.509 Certificate + * @return boolean indicating if the cert was self-signed + */ + private static boolean isSelfSigned(final X509Certificate cert) + throws SupplyChainValidatorException { + if (cert == null) { + throw new SupplyChainValidatorException("Certificate is null"); + } + try { + PublicKey key = cert.getPublicKey(); + cert.verify(key); + return true; + } catch (SignatureException | InvalidKeyException e) { + return false; + } catch (CertificateException | NoSuchAlgorithmException | NoSuchProviderException e) { + log.error("Exception occurred while checking if cert is self-signed", e); + return false; + } } } diff --git a/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/ReferenceManifestDetailsPageController.java b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/ReferenceManifestDetailsPageController.java index c2947c68..110bbd00 100644 --- a/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/ReferenceManifestDetailsPageController.java +++ b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/ReferenceManifestDetailsPageController.java @@ -1,6 +1,7 @@ package hirs.attestationca.portal.page.controllers; import hirs.attestationca.persist.DBServiceException; +import hirs.attestationca.persist.entity.manager.CACredentialRepository; import hirs.attestationca.persist.entity.manager.CertificateRepository; import hirs.attestationca.persist.entity.manager.ReferenceDigestValueRepository; import hirs.attestationca.persist.entity.manager.ReferenceManifestRepository; @@ -10,14 +11,14 @@ import hirs.attestationca.persist.entity.userdefined.rim.BaseReferenceManifest; import hirs.attestationca.persist.entity.userdefined.rim.EventLogMeasurements; import hirs.attestationca.persist.entity.userdefined.rim.ReferenceDigestValue; import hirs.attestationca.persist.entity.userdefined.rim.SupportReferenceManifest; -import hirs.attestationca.persist.service.SupplyChainValidationServiceImpl; +import hirs.attestationca.persist.service.ValidationManager; import hirs.attestationca.persist.validation.ReferenceManifestValidator; +import hirs.attestationca.persist.validation.SupplyChainCredentialValidator; import hirs.attestationca.persist.validation.SupplyChainValidatorException; import hirs.attestationca.portal.page.Page; import hirs.attestationca.portal.page.PageController; import hirs.attestationca.portal.page.PageMessages; import hirs.attestationca.portal.page.params.ReferenceManifestDetailsPageParams; -import hirs.attestationca.portal.page.utils.SupplyChainCredentialValidator; import hirs.utils.SwidResource; import hirs.utils.tpm.eventlog.TCGEventLog; import hirs.utils.tpm.eventlog.TpmPcrEvent; @@ -52,6 +53,7 @@ public class ReferenceManifestDetailsPageController extends PageController getRimDetailInfo(final UUID uuid, final ReferenceManifestRepository referenceManifestRepository, final ReferenceDigestValueRepository referenceDigestValueRepository, - final CertificateRepository certificateRepository) + final CertificateRepository certificateRepository, + final CACredentialRepository caCertificateRepository) throws IOException, CertificateException, NoSuchAlgorithmException { HashMap data = new HashMap<>(); @@ -146,7 +154,7 @@ public class ReferenceManifestDetailsPageController extends PageController getBaseRimInfo( final BaseReferenceManifest baseRim, final ReferenceManifestRepository referenceManifestRepository, - final CertificateRepository certificateRepository) + final CertificateRepository certificateRepository, + final CACredentialRepository caCertificateRepository) throws IOException, CertificateException, NoSuchAlgorithmException { HashMap data = new HashMap<>(); @@ -288,9 +298,7 @@ public class ReferenceManifestDetailsPageController extends PageController getComponentInfoFromPaccorOutput(final String paccorOutput) - throws IOException { - List componentInfoList = new ArrayList<>(); - - if (StringUtils.isNotEmpty(paccorOutput)) { - ObjectMapper objectMapper = new ObjectMapper(new JsonFactory()); - JsonNode rootNode = objectMapper.readTree(paccorOutput); - Iterator jsonComponentNodes - = rootNode.findValue("COMPONENTS").elements(); - while (jsonComponentNodes.hasNext()) { - JsonNode next = jsonComponentNodes.next(); - componentInfoList.add(new ComponentInfo( - getJSONNodeValueAsText(next, "MANUFACTURER"), - getJSONNodeValueAsText(next, "MODEL"), - getJSONNodeValueAsText(next, "SERIAL"), - getJSONNodeValueAsText(next, "REVISION"))); - } - } - - 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 getV2PaccorOutput( - final String paccorOutput) throws IOException { - List 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 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 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(); - } - return null; - } - - /** - * Checks if the platform credential is valid. - * - * @param pc The platform credential to verify. - * @param trustStore trust store holding trusted certificates. - * @param acceptExpired whether or not to accept expired certificates as valid. - * @return The result of the validation. - */ - @Override - public AppraisalStatus validatePlatformCredential(final PlatformCredential pc, - final KeyStore trustStore, - final boolean acceptExpired) { - final String baseErrorMessage = "Can't validate platform credential without "; - String message; - String certVerifyMsg; - if (pc == null) { - message = baseErrorMessage + "a platform credential\n"; - log.error(message); - return new AppraisalStatus(FAIL, message); - } - try { - if (trustStore == null || trustStore.size() == 0) { - message = baseErrorMessage + "an Issuer Cert in the Trust Store\n"; - log.error(message); - return new AppraisalStatus(FAIL, message); - } - } catch (KeyStoreException e) { - message = baseErrorMessage + "an initialized trust store"; - log.error(message); - return new AppraisalStatus(FAIL, message); - } - - X509AttributeCertificateHolder attributeCert = null; - try { - attributeCert = pc.getX509AttributeCertificateHolder(); - } catch (IOException e) { - message = "Could not retrieve X509 Attribute certificate"; - log.error(message, e); - return new AppraisalStatus(FAIL, message + " " + e.getMessage()); - } - - // check validity period, currently acceptExpired will also accept not yet - // valid certificates - if (!acceptExpired && !pc.isValidOn(new Date())) { - message = "Platform credential has expired"; - // if not valid at the current time - log.warn(message); - return new AppraisalStatus(FAIL, message); - } - - // verify cert against truststore - try { - certVerifyMsg = verifyCertificate(attributeCert, trustStore); - if (certVerifyMsg.isEmpty()) { - message = PLATFORM_VALID; - log.info(message); - return new AppraisalStatus(PASS, message); - } else { - message = String.format("Platform credential failed verification%n%s", - certVerifyMsg); - log.error(message); - return new AppraisalStatus(FAIL, message); - } - } catch (SupplyChainValidatorException scvEx) { - message = "An error occurred indicating the credential is not valid"; - log.warn(message, scvEx); - return new AppraisalStatus(FAIL, message + " " + scvEx.getMessage()); - } - } - - /** - * Checks if the platform credential's attributes are valid. - * @param platformCredential The platform credential to verify. - * @param deviceInfoReport The device info report containing - * serial number of the platform to be validated. - * @param endorsementCredential The endorsement credential supplied from the same - * identity request as the platform credential. - * @return The result of the validation. - */ - @Override - public AppraisalStatus validatePlatformCredentialAttributes( - final PlatformCredential platformCredential, - final DeviceInfoReport deviceInfoReport, - final EndorsementCredential endorsementCredential) { - final String baseErrorMessage = "Can't validate platform credential attributes without "; - String message; - if (platformCredential == null) { - message = baseErrorMessage + "a platform credential"; - log.error(message); - return new AppraisalStatus(FAIL, message); - } - if (deviceInfoReport == null) { - message = baseErrorMessage + "a device info report"; - log.error(message); - return new AppraisalStatus(FAIL, message); - } - if (endorsementCredential == null) { - message = baseErrorMessage + "an endorsement credential"; - log.error(message); - return new AppraisalStatus(FAIL, message); - } - - // Quick, early check if the platform credential references the endorsement credential - if (!endorsementCredential.getSerialNumber() - .equals(platformCredential.getHolderSerialNumber())) { - message = "Platform Credential holder serial number does not match " - + "the Endorsement Credential's serial number"; - log.error(message); - return new AppraisalStatus(FAIL, message); - } - - String credentialType = platformCredential.getCredentialType(); - if (PlatformCredential.CERTIFICATE_TYPE_2_0.equals(credentialType)) { - return validatePlatformCredentialAttributesV2p0(platformCredential, deviceInfoReport); - } - return validatePlatformCredentialAttributesV1p2(platformCredential, deviceInfoReport); - } - - /** - * Checks if the delta credential's attributes are valid. - * @param deltaPlatformCredential the delta credential to verify - * @param deviceInfoReport The device info report containing - * serial number of the platform to be validated. - * @param basePlatformCredential the base credential from the same identity request - * as the delta credential. - * @param deltaMapping delta certificates associated with the - * delta supply validation. - * @return the result of the validation. - */ - @Override - public AppraisalStatus validateDeltaPlatformCredentialAttributes( - final PlatformCredential deltaPlatformCredential, - final DeviceInfoReport deviceInfoReport, - final PlatformCredential basePlatformCredential, - final Map deltaMapping) { - String 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 pc : deltaMapping.keySet()) { - if (!basePlatformCredential.getPlatformSerial() - .equals(pc.getPlatformSerial())) { - message = String.format("Base and Delta platform serial " - + "numbers do not match (%s != %s)", - pc.getPlatformSerial(), - basePlatformCredential.getPlatformSerial()); - log.error(message); - return new AppraisalStatus(FAIL, message); - } - // none of the deltas should have the serial number of the base - if (!pc.isPlatformBase() && basePlatformCredential.getSerialNumber() - .equals(pc.getSerialNumber())) { - message = String.format("Delta Certificate with same serial number as base. (%s)", - pc.getSerialNumber()); - log.error(message); - return new AppraisalStatus(FAIL, message); - } - } - - // parse out the provided delta and its specific chain. - List origPcComponents - = new LinkedList<>(basePlatformCredential.getComponentIdentifiers()); - - return validateDeltaAttributesChainV2p0(deviceInfoReport, - deltaMapping, origPcComponents); - } - - private static AppraisalStatus validatePlatformCredentialAttributesV1p2( - final PlatformCredential platformCredential, - final DeviceInfoReport deviceInfoReport) { - - // check the device's board serial number, and compare against this - // platform credential's board serial number. - // Retrieve the various device serial numbers. - String credentialBoardSerialNumber = platformCredential.getPlatformSerial(); - String credentialChassisSerialNumber = platformCredential.getChassisSerialNumber(); - - HardwareInfo hardwareInfo = deviceInfoReport.getHardwareInfo(); - String deviceBaseboardSerialNumber = hardwareInfo.getBaseboardSerialNumber(); - String deviceChassisSerialNumber = hardwareInfo.getChassisSerialNumber(); - String deviceSystemSerialNumber = hardwareInfo.getSystemSerialNumber(); - - // log serial numbers that weren't collected. Force "not specified" serial numbers - // to be ignored in below case checks - Map deviceInfoSerialNumbers = new HashMap<>(); - - if (StringUtils.isEmpty(deviceBaseboardSerialNumber) - || DeviceInfoEnums.NOT_SPECIFIED.equalsIgnoreCase(deviceBaseboardSerialNumber)) { - log.error("Failed to retrieve device baseboard serial number"); - deviceBaseboardSerialNumber = null; - } else { - deviceInfoSerialNumbers.put("board serial number", deviceBaseboardSerialNumber); - log.info("Using device board serial number for validation: " - + deviceBaseboardSerialNumber); - } - - if (StringUtils.isEmpty(deviceChassisSerialNumber) - || DeviceInfoEnums.NOT_SPECIFIED.equalsIgnoreCase(deviceChassisSerialNumber)) { - log.error("Failed to retrieve device chassis serial number"); - } else { - deviceInfoSerialNumbers.put("chassis serial number", deviceChassisSerialNumber); - log.info("Using device chassis serial number for validation: " - + deviceChassisSerialNumber); - } - if (StringUtils.isEmpty(deviceSystemSerialNumber) - || DeviceInfoEnums.NOT_SPECIFIED.equalsIgnoreCase(deviceSystemSerialNumber)) { - log.error("Failed to retrieve device system serial number"); - } else { - deviceInfoSerialNumbers.put("system serial number", deviceSystemSerialNumber); - log.info("Using device system serial number for validation: " - + deviceSystemSerialNumber); - } - - AppraisalStatus status; - - // Test 1: If the board serial number or chassis is set on the PC, - // compare with each of the device serial numbers for any match - if (StringUtils.isNotEmpty(credentialBoardSerialNumber) - || StringUtils.isNotEmpty(credentialChassisSerialNumber)) { - status = validatePlatformSerialsWithDeviceSerials(credentialBoardSerialNumber, - credentialChassisSerialNumber, deviceInfoSerialNumbers); - // Test 2: If the board and chassis serial numbers are not set on the PC, - // compare the SHA1 hash of the device baseboard serial number to - // the certificate serial number - } else { - String message; - log.debug("Credential Serial Number was null"); - if (StringUtils.isEmpty(deviceBaseboardSerialNumber)) { - message = "Device Serial Number was null"; - log.error(message); - status = new AppraisalStatus(FAIL, message); - } else { - // Calculate the SHA1 hash of the UTF8 encoded baseboard serial number - BigInteger baseboardSha1 = new BigInteger(1, - DigestUtils.sha1(deviceBaseboardSerialNumber.getBytes(StandardCharsets.UTF_8))); - BigInteger certificateSerialNumber = platformCredential.getSerialNumber(); - - // compare the SHA1 hash of the baseboard serial number to the certificate SN - if (certificateSerialNumber != null - && certificateSerialNumber.equals(baseboardSha1)) { - log.info("Device Baseboard Serial Number matches " - + "the Certificate Serial Number"); - status = new AppraisalStatus(PASS, PLATFORM_ATTRIBUTES_VALID); - } else if (certificateSerialNumber != null - && certificateSerialNumber.equals( - baseboardSha1.clearBit(NUC_VARIABLE_BIT))) { - log.info("Warning! The Certificate serial number had the most significant " - + "bit truncated. 159 bits of it matched the device baseboard " - + "serial number."); - status = new AppraisalStatus(PASS, PLATFORM_ATTRIBUTES_VALID); - } else { - message = "The SHA1 hash of the Device Baseboard Serial Number " - + deviceBaseboardSerialNumber - + " did not match the Certificate's Serial Number"; - log.error(message); - status = new AppraisalStatus(FAIL, message); - - } - } - } - - return status; - } - - /** - * Validates device info report against the new platform credential. - * @param platformCredential the Platform Credential - * @param deviceInfoReport the Device Info Report - * @return either PASS or FAIL - */ - static AppraisalStatus validatePlatformCredentialAttributesV2p0( - final PlatformCredential platformCredential, - final DeviceInfoReport deviceInfoReport) { - boolean passesValidation = true; - StringBuilder resultMessage = new StringBuilder(); - - HardwareInfo hardwareInfo = deviceInfoReport.getHardwareInfo(); - - boolean fieldValidation; - fieldValidation = requiredPlatformCredentialFieldIsNonEmptyAndMatches( - "PlatformManufacturerStr", - platformCredential.getManufacturer(), - hardwareInfo.getManufacturer()); - - if (!fieldValidation) { - resultMessage.append("Platform manufacturer did not match\n"); - } - - passesValidation &= fieldValidation; - - fieldValidation = requiredPlatformCredentialFieldIsNonEmptyAndMatches( - "PlatformModel", - platformCredential.getModel(), - hardwareInfo.getProductName()); - - if (!fieldValidation) { - resultMessage.append("Platform model did not match\n"); - } - - passesValidation &= fieldValidation; - - fieldValidation = requiredPlatformCredentialFieldIsNonEmptyAndMatches( - "PlatformVersion", - platformCredential.getVersion(), - hardwareInfo.getVersion()); - - if (!fieldValidation) { - resultMessage.append("Platform version did not match\n"); - } - - passesValidation &= fieldValidation; - - // check PlatformSerial against both system-serial-number and baseboard-serial-number - fieldValidation = ( - (optionalPlatformCredentialFieldNullOrMatches( - "PlatformSerial", - platformCredential.getPlatformSerial(), - hardwareInfo.getSystemSerialNumber())) - || (optionalPlatformCredentialFieldNullOrMatches( - "PlatformSerial", - platformCredential.getPlatformSerial(), - hardwareInfo.getBaseboardSerialNumber()))); - - if (!fieldValidation) { - resultMessage.append("Platform serial did not match\n"); - } - - passesValidation &= fieldValidation; - - // Retrieve the list of all components from the Platform Credential - List allPcComponents - = new ArrayList<>(platformCredential.getComponentIdentifiers()); - - // All components listed in the Platform Credential must have a manufacturer and model - for (ComponentIdentifier pcComponent : allPcComponents) { - fieldValidation = !hasEmptyValueForRequiredField("componentManufacturer", - pcComponent.getComponentManufacturer()); - - if (!fieldValidation) { - resultMessage.append("Component manufacturer is empty\n"); - } - - passesValidation &= fieldValidation; - - fieldValidation = !hasEmptyValueForRequiredField("componentModel", - pcComponent.getComponentModel()); - - if (!fieldValidation) { - resultMessage.append("Component model is empty\n"); - } - - passesValidation &= fieldValidation; - } - - // There is no need to do comparisons with components that are invalid because - // they did not have a manufacturer or model. - List validPcComponents = allPcComponents.stream() - .filter(identifier -> identifier.getComponentManufacturer() != null - && identifier.getComponentModel() != null) - .collect(Collectors.toList()); - - String paccorOutputString = deviceInfoReport.getPaccorOutputString(); - String unmatchedComponents; - try { - List componentInfoList - = getComponentInfoFromPaccorOutput(paccorOutputString); - unmatchedComponents = validateV2p0PlatformCredentialComponentsExpectingExactMatch( - validPcComponents, componentInfoList); - fieldValidation &= unmatchedComponents.isEmpty(); - } catch (IOException e) { - final String baseErrorMessage = "Error parsing JSON output from PACCOR: "; - log.error(baseErrorMessage + e.toString()); - log.error("PACCOR output string:\n" + paccorOutputString); - 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 - int counter = 0; - for (ComponentIdentifier ci : validPcComponents) { - counter++; - additionalInfo.append(String.format("%d;", ci.hashCode())); - } - if (counter > 0) { - additionalInfo.insert(0, "COMPID="); - additionalInfo.append(counter); - } - } - - passesValidation &= fieldValidation; - - if (passesValidation) { - return new AppraisalStatus(PASS, PLATFORM_ATTRIBUTES_VALID); - } else { - return new AppraisalStatus(FAIL, resultMessage.toString(), additionalInfo.toString()); - } - } - - /** - * The main purpose of this method, the in process of validation, is to - * pick out the changes that lead to the delta cert and make sure the changes - * are valid. - * - * @param deviceInfoReport The paccor profile of device being validated against. - * @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. - */ - @SuppressWarnings("methodlength") - static AppraisalStatus validateDeltaAttributesChainV2p0( - final DeviceInfoReport deviceInfoReport, - final Map deltaMapping, - final List origPcComponents) { - boolean fieldValidation = true; - StringBuilder resultMessage = new StringBuilder(); - String tempStringMessage = ""; - List validOrigPcComponents = origPcComponents.stream() - .filter(identifier -> identifier.getComponentManufacturer() != null - && identifier.getComponentModel() != null) - .collect(Collectors.toList()); - List chainCertificates = new LinkedList<>(deltaMapping.keySet()); - - // map the components throughout the chain - List baseCompList = new LinkedList<>(validOrigPcComponents); - - Collections.sort(chainCertificates, new Comparator() { - @Override - public int compare(final PlatformCredential obj1, - final PlatformCredential obj2) { - if (obj1 == null) { - return 0; - } - if (obj2 == null) { - return 0; - } - if (obj1.getBeginValidity() == null || obj2.getBeginValidity() == null) { - return 0; - } - return obj1.getBeginValidity().compareTo(obj2.getBeginValidity()); - } - }); - // start of some changes - resultMessage.append("There are errors with Delta " - + "Component Statuses:\n"); - List leftOverDeltas = new ArrayList<>(); - List absentSerialNum = new ArrayList<>(); - tempStringMessage = validateDeltaChain(deltaMapping, baseCompList, - leftOverDeltas, absentSerialNum, chainCertificates); - - // check if there were any issues - if (!tempStringMessage.isEmpty()) { - resultMessage.append(tempStringMessage); - fieldValidation = false; - } - - // finished up - List certificateList = null; - SupplyChainValidation scv = null; - StringBuilder deltaSb = new StringBuilder(); - - // 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().getComponentIdentifier(); - baseCiV2 = (ComponentIdentifierV2) ci; - classFound = classValue.equals(baseCiV2.getComponentClass() - .getComponentIdentifier()); - if (classFound) { - if (isMatch(ciV2, baseCiV2)) { - if (ciV2.isAdded() || ciV2.isModified()) { - // since the base list doesn't have this ci - // just add the delta - baseCompList.add(deltaCi); - break; - } - if (ciV2.isRemoved()) { - baseCompList.remove(ciV2); - break; - } - // 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())); - } - } - } - } - - if (!fieldValidation || !deltaSb.toString().isEmpty()) { - deltaSb.insert(0, "COMPID="); - return new AppraisalStatus(FAIL, resultMessage.toString(), deltaSb.toString()); - } - - String paccorOutputString = deviceInfoReport.getPaccorOutputString(); - String unmatchedComponents; - try { - // compare based on component class - List componentInfoList = getV2PaccorOutput(paccorOutputString); - // this is what I want to rewrite - unmatchedComponents = validateV2PlatformCredentialAttributes( - baseCompList, - componentInfoList); - fieldValidation &= unmatchedComponents.isEmpty(); - } catch (IOException ioEx) { - final String baseErrorMessage = "Error parsing JSON output from PACCOR: "; - log.error(baseErrorMessage + ioEx.toString()); - log.error("PACCOR output string:\n" + paccorOutputString); - return new AppraisalStatus(ERROR, baseErrorMessage + ioEx.getMessage()); - } - StringBuilder additionalInfo = new StringBuilder(); - if (!fieldValidation) { - resultMessage = new StringBuilder(); - resultMessage.append("There are unmatched components:\n"); - resultMessage.append(unmatchedComponents); - - // pass information of which ones failed in additionInfo - int counter = 0; - for (ComponentIdentifier ci : baseCompList) { - counter++; - additionalInfo.append(String.format("%d;", ci.hashCode())); - } - if (counter > 0) { - additionalInfo.insert(0, "COMPID="); - additionalInfo.append(counter); - } - } - - if (fieldValidation) { - return new AppraisalStatus(PASS, PLATFORM_ATTRIBUTES_VALID); - } else { - return new AppraisalStatus(FAIL, resultMessage.toString(), additionalInfo.toString()); - } - } - - private static String validateV2PlatformCredentialAttributes( - final List fullDeltaChainComponents, - final List allDeviceInfoComponents) { - ComponentIdentifierV2 ciV2; - StringBuilder invalidPcIds = new StringBuilder(); - List subCompIdList = fullDeltaChainComponents - .stream().collect(Collectors.toList()); - List subCompInfoList = allDeviceInfoComponents - .stream().collect(Collectors.toList()); - - // Delta is the baseline - for (ComponentInfo cInfo : allDeviceInfoComponents) { - for (ComponentIdentifier cId : fullDeltaChainComponents) { - ciV2 = (ComponentIdentifierV2) cId; - if (cInfo.getComponentClass().contains( - ciV2.getComponentClass().getComponentIdentifier()) - && isMatch(cId, cInfo)) { - subCompIdList.remove(cId); - subCompInfoList.remove(cInfo); - } - } - } - - 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 - fullDeltaChainComponents.clear(); - for (ComponentIdentifier ci : subCompIdList) { - if (ci.isVersion2() && PciIds.DB.isReady()) { - ci = PciIds.translate((ComponentIdentifierV2) ci); - } - log.error("Unmatched component: " + ci); - fullDeltaChainComponents.add(ci); - invalidPcIds.append(String.format( - "Manufacturer=%s, Model=%s, Serial=%s, Revision=%s;%n", - ci.getComponentManufacturer(), - ci.getComponentModel(), - ci.getComponentSerial(), - ci.getComponentRevision())); - } - } - - return invalidPcIds.toString(); - } - - /** - * Compares the component information from the device info report against those of the - * platform credential. All components in the platform credential should exactly match one - * component in the device info report. The device info report is allowed to have extra - * 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 - */ - private static String validateV2p0PlatformCredentialComponentsExpectingExactMatch( - final List untrimmedPcComponents, - final List allDeviceInfoComponents) { - // For each manufacturer listed in the platform credential, create two lists: - // 1. a list of components listed in the platform credential for the manufacturer, and - // 2. a list of components listed in the device info for the same manufacturer - // Then eliminate matches from both lists. Finally, decide if the validation passes based - // on the leftovers in the lists and the policy in place. - final List pcComponents = new ArrayList<>(); - for (ComponentIdentifier component : untrimmedPcComponents) { - if (component.getComponentManufacturer() != null) { - component.setComponentManufacturer(new DERUTF8String( - component.getComponentManufacturer().getString().trim())); - } - if (component.getComponentModel() != null) { - component.setComponentModel(new DERUTF8String( - component.getComponentModel().getString().trim())); - } - if (component.getComponentSerial() != null) { - component.setComponentSerial(new DERUTF8String( - component.getComponentSerial().getString().trim())); - } - if (component.getComponentRevision() != null) { - component.setComponentRevision(new DERUTF8String( - component.getComponentRevision().getString().trim())); - } - pcComponents.add(component); - } - - log.info("Validating the following Platform Cert components..."); - pcComponents.forEach(component -> log.info(component.toString())); - log.info("...against the the following DeviceInfoReport components:"); - allDeviceInfoComponents.forEach(component -> log.info(component.toString())); - Set manufacturerSet = new HashSet<>(); - pcComponents.forEach(pcComp -> manufacturerSet.add(pcComp.getComponentManufacturer())); - - // Create a list for unmatched components across all manufacturers to display at the end. - List pcUnmatchedComponents = new ArrayList<>(); - - for (DERUTF8String derUtf8Manufacturer : manufacturerSet) { - List pcComponentsFromManufacturer - = pcComponents.stream().filter(compIdentifier - -> compIdentifier.getComponentManufacturer().equals(derUtf8Manufacturer)) - .collect(Collectors.toList()); - - String pcManufacturer = derUtf8Manufacturer.getString(); - List deviceInfoComponentsFromManufacturer - = allDeviceInfoComponents.stream().filter(componentInfo - -> componentInfo.getComponentManufacturer().equals(pcManufacturer)) - .collect(Collectors.toList()); - // For each component listed in the platform credential from this manufacturer - // find the ones that specify a serial number so we can match the most specific ones - // first. - List pcComponentsFromManufacturerWithSerialNumber - = pcComponentsFromManufacturer.stream().filter(compIdentifier - -> compIdentifier.getComponentSerial() != null - && StringUtils.isNotEmpty(compIdentifier.getComponentSerial().getString())) - .collect(Collectors.toList()); - // Now match up the components from the device info that are from the same - // manufacturer and have a serial number. As matches are found, remove them from - // both lists. - for (ComponentIdentifier pcComponent - : pcComponentsFromManufacturerWithSerialNumber) { - Optional first - = deviceInfoComponentsFromManufacturer.stream() - .filter(componentInfo - -> StringUtils.isNotEmpty(componentInfo.getComponentSerial())) - .filter(componentInfo -> componentInfo.getComponentSerial() - .equals(pcComponent.getComponentSerial().getString())).findFirst(); - - if (first.isPresent()) { - ComponentInfo potentialMatch = first.get(); - if (isMatch(pcComponent, potentialMatch)) { - pcComponentsFromManufacturer.remove(pcComponent); - deviceInfoComponentsFromManufacturer.remove(potentialMatch); - } - } - } - // For each component listed in the platform credential from this manufacturer - // find the ones that specify value for the revision field so we can match the most - // specific ones first. - List pcComponentsFromManufacturerWithRevision - = pcComponentsFromManufacturer.stream().filter(compIdentifier - -> compIdentifier.getComponentRevision() != null - && StringUtils.isNotEmpty(compIdentifier.getComponentRevision().getString())) - .collect(Collectors.toList()); - // Now match up the components from the device info that are from the same - // manufacturer and specify a value for the revision field. As matches are found, - // remove them from both lists. - for (ComponentIdentifier pcComponent - : pcComponentsFromManufacturerWithRevision) { - Optional first - = deviceInfoComponentsFromManufacturer.stream() - .filter(info -> StringUtils.isNotEmpty(info.getComponentRevision())) - .filter(info -> info.getComponentRevision() - .equals(pcComponent.getComponentRevision().getString())) - .findFirst(); - - if (first.isPresent()) { - ComponentInfo potentialMatch = first.get(); - if (isMatch(pcComponent, potentialMatch)) { - pcComponentsFromManufacturer.remove(pcComponent); - deviceInfoComponentsFromManufacturer.remove(potentialMatch); - } - } - } - // The remaining components from the manufacturer have only the 2 required fields so - // just match them. - List templist = new ArrayList<>(pcComponentsFromManufacturer); - for (ComponentIdentifier ci : templist) { - Iterator diComponentIter - = deviceInfoComponentsFromManufacturer.iterator(); - while (diComponentIter.hasNext()) { - ComponentInfo potentialMatch = diComponentIter.next(); - if (isMatch(ci, potentialMatch)) { - pcComponentsFromManufacturer.remove(ci); - diComponentIter.remove(); - } - } - } - pcUnmatchedComponents.addAll(pcComponentsFromManufacturer); - } - - if (!pcUnmatchedComponents.isEmpty()) { - untrimmedPcComponents.clear(); - StringBuilder sb = new StringBuilder(); - log.error(String.format("Platform Credential contained %d unmatched components:", - pcUnmatchedComponents.size())); - - int unmatchedComponentCounter = 1; - for (ComponentIdentifier unmatchedComponent : pcUnmatchedComponents) { - if (unmatchedComponent.isVersion2() && PciIds.DB.isReady()) { - unmatchedComponent = - PciIds.translate((ComponentIdentifierV2) unmatchedComponent); - } - log.error("Unmatched component " + unmatchedComponentCounter++ + ": " - + unmatchedComponent); - sb.append(String.format("Manufacturer=%s, Model=%s, Serial=%s, Revision=%s;%n", - unmatchedComponent.getComponentManufacturer(), - unmatchedComponent.getComponentModel(), - unmatchedComponent.getComponentSerial(), - unmatchedComponent.getComponentRevision())); - unmatchedComponent.setValidationResult(false); - untrimmedPcComponents.add(unmatchedComponent); - } - return sb.toString(); - } - return Strings.EMPTY; - } - - /** - * Returns true if fieldValue is null or empty. - * @param description description of the value - * @param fieldValue value of the field - * @return true if fieldValue is null or empty; false otherwise - */ - private static boolean hasEmptyValueForRequiredField(final String description, - final String fieldValue) { - if (StringUtils.isEmpty(fieldValue)) { - log.error("Required field was empty or null in Platform Credential: " - + description); - return true; - } - return false; - } - - /** - * Returns true if fieldValue is null or empty. - * @param description description of the value - * @param fieldValue value of the field - * @return true if fieldValue is null or empty; false otherwise - */ - private static boolean hasEmptyValueForRequiredField(final String description, - final DERUTF8String fieldValue) { - if (fieldValue == null || StringUtils.isEmpty(fieldValue.getString().trim())) { - log.error("Required field was empty or null in Platform Credential: " - + description); - return true; - } - return false; - } - - /** - * Validates the information supplied for the Platform Credential. This - * method checks if the field is required and therefore if the value is - * present then verifies that the values match. - * @param platformCredentialFieldName name of field to be compared - * @param platformCredentialFieldValue first value to compare - * @param otherValue second value to compare - * @return true if values match - */ - private static boolean requiredPlatformCredentialFieldIsNonEmptyAndMatches( - final String platformCredentialFieldName, - final String platformCredentialFieldValue, - final String otherValue) { - if (hasEmptyValueForRequiredField(platformCredentialFieldName, - platformCredentialFieldValue)) { - return false; - } - - return platformCredentialFieldMatches(platformCredentialFieldName, - platformCredentialFieldValue, otherValue); - } - - /** - * Validates the information supplied for the Platform Credential. This - * method checks if the value is present then verifies that the values match. - * If not present, then returns true. - * @param platformCredentialFieldName name of field to be compared - * @param platformCredentialFieldValue first value to compare - * @param otherValue second value to compare - * @return true if values match or null - */ - private static boolean optionalPlatformCredentialFieldNullOrMatches( - final String platformCredentialFieldName, - final String platformCredentialFieldValue, - final String otherValue) { - if (platformCredentialFieldValue == null) { - return true; - } - - return platformCredentialFieldMatches(platformCredentialFieldName, - platformCredentialFieldValue, otherValue); - } - - private static boolean platformCredentialFieldMatches( - final String platformCredentialFieldName, - final String platformCredentialFieldValue, - final String otherValue) { - String trimmedFieldValue = platformCredentialFieldValue.trim(); - String trimmedOtherValue = otherValue.trim(); - - if (!trimmedFieldValue.equals(trimmedOtherValue)) { - log.debug(String.format("%s field in Platform Credential (%s) does not match " - + "a related field in the DeviceInfoReport (%s)", - platformCredentialFieldName, trimmedFieldValue, trimmedOtherValue)); - return false; - } - - log.debug(String.format("%s field in Platform Credential matches " - + "a related field in the DeviceInfoReport (%s)", - platformCredentialFieldName, trimmedFieldValue) - ); - - return true; - } - - /** - * 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 ComponentIdentifier pcComponent, - final ComponentInfo potentialMatch) { - boolean matchesSoFar = true; - - matchesSoFar &= isMatchOrEmptyInPlatformCert( - potentialMatch.getComponentManufacturer(), - pcComponent.getComponentManufacturer() - ); - - matchesSoFar &= isMatchOrEmptyInPlatformCert( - potentialMatch.getComponentModel(), - pcComponent.getComponentModel() - ); - - matchesSoFar &= isMatchOrEmptyInPlatformCert( - potentialMatch.getComponentSerial(), - pcComponent.getComponentSerial() - ); - - matchesSoFar &= isMatchOrEmptyInPlatformCert( - potentialMatch.getComponentRevision(), - pcComponent.getComponentRevision() - ); - - 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) { - if (valueInPlatformCert == null || StringUtils.isEmpty(valueInPlatformCert.getString())) { - return true; - } - 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. - * @param credentialBoardSerialNumber the PC board S/N - * @param credentialChassisSerialNumber the PC chassis S/N - * @param deviceInfoSerialNumbers the map of device info serial numbers with descriptions. - * @return the changed validation status - */ - private static AppraisalStatus validatePlatformSerialsWithDeviceSerials( - final String credentialBoardSerialNumber, final String credentialChassisSerialNumber, - final Map deviceInfoSerialNumbers) { - boolean boardSerialNumberFound = false; - boolean chassisSerialNumberFound = false; - - if (StringUtils.isNotEmpty(credentialBoardSerialNumber)) { - boardSerialNumberFound = deviceInfoContainsPlatformSerialNumber( - credentialBoardSerialNumber, "board serial number", deviceInfoSerialNumbers); - } - if (StringUtils.isNotEmpty(credentialChassisSerialNumber)) { - chassisSerialNumberFound = deviceInfoContainsPlatformSerialNumber( - credentialChassisSerialNumber, - "chassis serial number", deviceInfoSerialNumbers); - } - - if (boardSerialNumberFound || chassisSerialNumberFound) { - log.info("The platform credential's board or chassis serial number matched" - + " with a serial number from the client's device information"); - return new AppraisalStatus(PASS, PLATFORM_ATTRIBUTES_VALID); - } - log.error("The platform credential's board and chassis serial numbers did" - + " not match with any device info's serial numbers"); - - return new AppraisalStatus(FAIL, "Platform serial did not match device info"); - } - - - /** - * Checks if a platform credential's serial number matches ANY of the device information's - * set of serial numbers. - * @param platformSerialNumber the platform serial number to compare - * @param platformSerialNumberDescription description of the serial number for logging purposes. - * @param deviceInfoSerialNumbers the map of device info serial numbers - * (key = description, value = serial number) - * @return true if the platform serial number was found (case insensitive search), - * false otherwise - */ - private static boolean deviceInfoContainsPlatformSerialNumber( - final String platformSerialNumber, final String platformSerialNumberDescription, - final Map deviceInfoSerialNumbers) { - // check to see if the platform serial number is contained in the map of device info's - // serial numbers - for (Map.Entry entry : deviceInfoSerialNumbers.entrySet()) { - if (entry.getValue().equalsIgnoreCase(platformSerialNumber)) { - log.info("Device info contained platform {} {}" - + " in the device info's {}", platformSerialNumberDescription, - platformSerialNumber, entry.getKey()); - return true; - } - } - - log.warn("Platform {}, {}, did not match any device info serial numbers", - platformSerialNumberDescription, platformSerialNumber); - return false; - } - - /** - * Checks if the endorsement credential is valid. - * - * @param ec the endorsement credential to verify. - * @param trustStore trust store holding trusted trusted certificates. - * @param acceptExpired whether or not to accept expired and not yet valid certificates - * as valid. - * @return the result of the validation. - */ - @Override - public AppraisalStatus validateEndorsementCredential(final EndorsementCredential ec, - final KeyStore trustStore, - final boolean acceptExpired) { - final String baseErrorMessage = "Can't validate endorsement credential attributes without "; - String message; - if (ec == null) { - message = baseErrorMessage + "an endorsement credential"; - log.error(message); - return new AppraisalStatus(FAIL, message); - } - if (trustStore == null) { - message = baseErrorMessage + "a trust store"; - log.error(message); - return new AppraisalStatus(FAIL, message); - } - - try { - X509Certificate verifiableCert = ec.getX509Certificate(); - - // check validity period, currently acceptExpired will also accept not yet - // valid certificates - if (!acceptExpired) { - verifiableCert.checkValidity(); - } - - if (verifyCertificate(verifiableCert, trustStore)) { - return new AppraisalStatus(PASS, ENDORSEMENT_VALID); - } else { - return new AppraisalStatus(FAIL, "Endorsement credential does not have a valid " - + "signature chain in the trust store"); - } - } catch (IOException e) { - message = "Couldn't retrieve X509 certificate from endorsement credential"; - log.error(message, e); - return new AppraisalStatus(ERROR, message + " " + e.getMessage()); - } catch (SupplyChainValidatorException e) { - message = "An error occurred indicating the credential is not valid"; - log.warn(message, e); - return new AppraisalStatus(ERROR, message + " " + e.getMessage()); - } catch (CertificateExpiredException e) { - message = "The endorsement credential is expired"; - log.warn(message, e); - return new AppraisalStatus(FAIL, message + " " + e.getMessage()); - } catch (CertificateNotYetValidException e) { - message = "The endorsement credential is not yet valid"; - log.warn(message, e); - return new AppraisalStatus(FAIL, message + " " + e.getMessage()); - } - } - - /** - * Attempts to check if the certificate is validated by certificates in a cert chain. The cert - * chain is expected to be stored in a non-ordered KeyStore (trust store). If the signing - * certificate for the target cert is found, but it is an intermediate cert, the validation will - * continue to try to find the signing cert of the intermediate cert. It will continue searching - * until it follows the chain up to a root (self-signed) cert. - * - * @param cert - * certificate to validate - * @param trustStore - * trust store holding trusted root certificates and intermediate certificates - * @return the certificate chain if validation is successful - * @throws SupplyChainValidatorException - * if the verification is not successful - */ - public static String verifyCertificate(final X509AttributeCertificateHolder cert, - final KeyStore trustStore) throws SupplyChainValidatorException { - try { - if (cert == null || trustStore == null) { - throw new SupplyChainValidatorException("Certificate or trust store is null"); - } else if (trustStore.size() == 0) { - throw new SupplyChainValidatorException("Truststore is empty"); - } - } catch (KeyStoreException e) { - log.error("Error accessing trust store: " + e.getMessage()); - } - - try { - Set trustedCerts = new HashSet<>(); - - Enumeration alias = trustStore.aliases(); - - while (alias.hasMoreElements()) { - trustedCerts.add((X509Certificate) trustStore.getCertificate(alias.nextElement())); - } - - String certChainValidated = validateCertChain(cert, trustedCerts); - if (!certChainValidated.isEmpty()) { - log.error("Cert chain could not be validated"); - } - return certChainValidated; - } catch (KeyStoreException e) { - throw new SupplyChainValidatorException("Error with the trust store", e); - } - } - - /** - * Attempts to check if the certificate is validated by certificates in a cert chain. The cert - * chain is expected to be stored in a non-ordered KeyStore (trust store). If the signing - * certificate for the target cert is found, but it is an intermediate cert, the validation will - * continue to try to find the signing cert of the intermediate cert. It will continue searching - * until it follows the chain up to a root (self-signed) cert. - * - * @param cert - * certificate to validate - * @param trustStore - * trust store holding trusted root certificates and intermediate certificates - * @return the certificate chain if validation is successful - * @throws SupplyChainValidatorException - * if the verification is not successful - */ - public static boolean verifyCertificate(final X509Certificate cert, - final KeyStore trustStore) throws SupplyChainValidatorException { - try { - if (cert == null || trustStore == null) { - throw new SupplyChainValidatorException("Certificate or trust store is null"); - } else if (trustStore.size() == 0) { - throw new SupplyChainValidatorException("Truststore is empty"); - } - } catch (KeyStoreException e) { - log.error("Error accessing trust store: " + e.getMessage()); - } - - try { - Set trustedCerts = new HashSet<>(); - - Enumeration alias = trustStore.aliases(); - - while (alias.hasMoreElements()) { - trustedCerts.add((X509Certificate) trustStore.getCertificate(alias.nextElement())); - } - - return validateCertChain(cert, trustedCerts).isEmpty(); - } catch (KeyStoreException e) { - log.error("Error accessing keystore", e); - throw new SupplyChainValidatorException("Error with the trust store", e); - } - - } - - /** - * Attempts to check if an attribute certificate is validated by certificates in a cert chain. - * The cert chain is represented as a Set of X509Certificates. If the signing certificate for - * the target cert is found, but it is an intermediate cert, the validation will continue to try - * to find the signing cert of the intermediate cert. It will continue searching until it - * follows the chain up to a root (self-signed) cert. - * - * @param cert - * certificate to validate - * @param additionalCerts - * Set of certs to validate against - * @return String status of the cert chain validation - - * blank if successful, error message otherwise - * @throws SupplyChainValidatorException tried to validate using null certificates - */ - public static String validateCertChain(final X509AttributeCertificateHolder cert, - final Set additionalCerts) - throws SupplyChainValidatorException { - if (cert == null || additionalCerts == null) { - throw new SupplyChainValidatorException( - "Certificate or validation certificates are null"); - } - final String intCAError = "Intermediate signing cert found, check for CA cert"; - String foundRootOfCertChain = ""; - X509Certificate nextInChain = null; - - do { - for (X509Certificate trustedCert : additionalCerts) { - boolean issuerMatchesSubject = false; - boolean signatureMatchesPublicKey = false; - if (nextInChain != null) { - issuerMatchesSubject = issuerMatchesSubjectDN(nextInChain, trustedCert); - signatureMatchesPublicKey = signatureMatchesPublicKey(nextInChain, - trustedCert); - } else { - issuerMatchesSubject = issuerMatchesSubjectDN(cert, trustedCert); - signatureMatchesPublicKey = signatureMatchesPublicKey(cert, trustedCert); - } - - if (issuerMatchesSubject && signatureMatchesPublicKey) { - if (isSelfSigned(trustedCert)) { - log.info("CA Root found."); - return ""; - } else { - foundRootOfCertChain = intCAError; - nextInChain = trustedCert; - break; - } - } else { - if (!issuerMatchesSubject) { - foundRootOfCertChain = "Issuer DN does not match Subject DN"; - } - if (!signatureMatchesPublicKey) { - foundRootOfCertChain = "Certificate signature failed to verify"; - } - } - } - } while (foundRootOfCertChain.equals(intCAError)); - - log.error(foundRootOfCertChain); - return foundRootOfCertChain; - } - - /** - * Attempts to check if a public-key certificate is validated by certificates in a cert chain. - * The cert chain is represented as a Set of X509Certificates. If the signing certificate for - * the target cert is found, but it is an intermediate cert, the validation will continue to try - * to find the signing cert of the intermediate cert. It will continue searching until it - * follows the chain up to a root (self-signed) cert. - * - * @param cert - * certificate to validate - * @param additionalCerts - * Set of certs to validate against - * @return String status of the cert chain validation - - * blank if successful, error message otherwise - * @throws SupplyChainValidatorException tried to validate using null certificates - */ - public static String validateCertChain(final X509Certificate cert, - final Set additionalCerts) throws SupplyChainValidatorException { - if (cert == null || additionalCerts == null) { - throw new SupplyChainValidatorException( - "Certificate or validation certificates are null"); - } - final String intCAError = "Intermediate signing cert found, check for CA cert"; - String foundRootOfCertChain = ""; - X509Certificate startOfChain = cert; - - do { - for (X509Certificate trustedCert : additionalCerts) { - boolean issuerMatchesSubject = issuerMatchesSubjectDN(startOfChain, trustedCert); - boolean signatureMatchesPublicKey = signatureMatchesPublicKey(startOfChain, - trustedCert); - if (issuerMatchesSubject && signatureMatchesPublicKey) { - if (isSelfSigned(trustedCert)) { - log.info("CA Root found."); - return ""; - } else { - foundRootOfCertChain = intCAError; - startOfChain = trustedCert; - break; - } - } else { - if (!issuerMatchesSubject) { - foundRootOfCertChain = "Issuer DN does not match Subject DN"; - } - if (!signatureMatchesPublicKey) { - foundRootOfCertChain = "Certificate signature failed to verify"; - } - } - } - } while (foundRootOfCertChain.equals(intCAError)); - - log.warn(foundRootOfCertChain); - return foundRootOfCertChain; - } - - private static String validateDeltaChain( - final Map deltaMapping, - final List baseCompList, - final List leftOvers, - final List absentSerials, - final List chainCertificates) { - StringBuilder resultMessage = new StringBuilder(); - List 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 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 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.getValidationResult() != PASS) { - failureMsg.append(scv.getMessage()); - } - deltaMapping.put(delta, new SupplyChainValidation( - SupplyChainValidation.ValidationType.PLATFORM_CREDENTIAL, - 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.getValidationResult() != PASS) { - failureMsg.append(scv.getMessage()); - } - deltaMapping.put(delta, new SupplyChainValidation( - SupplyChainValidation.ValidationType.PLATFORM_CREDENTIAL, - 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.getValidationResult() != PASS) { - failureMsg.append(scv.getMessage()); - } - deltaMapping.put(delta, new SupplyChainValidation( - SupplyChainValidation.ValidationType.PLATFORM_CREDENTIAL, - FAIL, - certificateList, - failureMsg.toString())); - } else { - // have to add in case later it is removed - chainCiMapping.put(ciSerial, ci); - } - } - } - } else { - if (ci.isVersion2() && ((ComponentIdentifierV2) ci).isModified()) { - ComponentIdentifierV2 ciV2 = (ComponentIdentifierV2) ci; - // Look for singular component of same make/model/class - ComponentIdentifier candidate = null; - for (ComponentIdentifier search : absentSerials) { - if (!search.isVersion2()) { - continue; - } - ComponentIdentifierV2 noSerialV2 = (ComponentIdentifierV2) search; - - if (noSerialV2.getComponentClass().getComponentIdentifier().equals( - ciV2.getComponentClass().getComponentIdentifier()) - && isMatch(noSerialV2, ciV2)) { - if (candidate == null) { - candidate = noSerialV2; - } else { - // This only works if there is one matching component - candidate = null; - break; - } - } - } - - if (candidate != null) { - absentSerials.remove(candidate); - absentSerials.add(ciV2); - } else { - leftOvers.add(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. - * - * @param cert - * the attribute certificate with the signature to validate - * @param signingCert - * the certificate with the public key to validate - * @return boolean indicating if the names - * @throws SupplyChainValidatorException tried to validate using null certificates - */ - public static boolean issuerMatchesSubjectDN(final X509AttributeCertificateHolder cert, - final X509Certificate signingCert) throws SupplyChainValidatorException { - if (cert == null || signingCert == null) { - throw new SupplyChainValidatorException("Certificate or signing certificate is null"); - } - String signingCertSubjectDN = signingCert.getSubjectX500Principal().getName(); - X500Name namedSubjectDN = new X500Name(signingCertSubjectDN); - - X500Name issuerDN = cert.getIssuer().getNames()[0]; - - // equality check ignore DN component ordering - return issuerDN.equals(namedSubjectDN); - } - - /** - * Checks if the issuer info of a public-key cert matches the supposed signing cert's - * distinguished name. - * - * @param cert - * the public-key certificate with the signature to validate - * @param signingCert - * the certificate with the public key to validate - * @return boolean indicating if the names - * @throws SupplyChainValidatorException tried to validate using null certificates - */ - public static boolean issuerMatchesSubjectDN(final X509Certificate cert, - final X509Certificate signingCert) throws SupplyChainValidatorException { - if (cert == null || signingCert == null) { - throw new SupplyChainValidatorException("Certificate or signing certificate is null"); - } - String signingCertSubjectDN = signingCert.getSubjectX500Principal(). - getName(X500Principal.RFC1779); - X500Name namedSubjectDN = new X500Name(signingCertSubjectDN); - - String certIssuerDN = cert.getIssuerX500Principal().getName(); - X500Name namedIssuerDN = new X500Name(certIssuerDN); - - // equality check ignore DN component ordering - return namedIssuerDN.equals(namedSubjectDN); - } - - /** - * Checks if the signature of an attribute cert is validated against the signing cert's public - * key. - * - * @param cert - * the public-key certificate with the signature to validate - * @param signingCert - * the certificate with the public key to validate - * @return boolean indicating if the validation passed - * @throws SupplyChainValidatorException tried to validate using null certificates - */ - public static boolean signatureMatchesPublicKey(final X509Certificate cert, - final X509Certificate signingCert) throws SupplyChainValidatorException { - if (cert == null || signingCert == null) { - throw new SupplyChainValidatorException("Certificate or signing certificate is null"); - } - try { - cert.verify(signingCert.getPublicKey(), BouncyCastleProvider.PROVIDER_NAME); - return true; - } catch (InvalidKeyException e) { - log.info("Incorrect key given to validate this cert's signature"); - } catch (CertificateException e) { - log.info("Encoding error while validating this cert's signature"); - } catch (NoSuchAlgorithmException e) { - log.info("Unsupported signature algorithm found during validation"); - } catch (NoSuchProviderException e) { - log.info("Incorrect provider for cert signature validation"); - } catch (SignatureException e) { - log.info(String.format("%s.verify(%s)", cert.getSubjectX500Principal(), - signingCert.getSubjectX500Principal())); - } - return false; - - } - - /** - * Checks if the signature of a public-key cert is validated against the signing cert's public - * key. - * - * @param cert - * the attribute certificate with the signature to validate - * @param signingCert - * the certificate with the public key to validate - * @return boolean indicating if the validation passed - * @throws SupplyChainValidatorException tried to validate using null certificates - */ - public static boolean signatureMatchesPublicKey(final X509AttributeCertificateHolder cert, - final X509Certificate signingCert) throws SupplyChainValidatorException { - if (signingCert == null) { - throw new SupplyChainValidatorException("Signing certificate is null"); - } - return signatureMatchesPublicKey(cert, signingCert.getPublicKey()); - } - - /** - * Checks if an X509 Attribute Certificate is valid directly against a public key. - * - * @param cert - * the attribute certificate with the signature to validate - * @param signingKey - * the key to use to check the attribute cert - * @return boolean indicating if the validation passed - * @throws SupplyChainValidatorException tried to validate using null certificates - */ - public static boolean signatureMatchesPublicKey(final X509AttributeCertificateHolder cert, - final PublicKey signingKey) throws SupplyChainValidatorException { - if (cert == null || signingKey == null) { - throw new SupplyChainValidatorException("Certificate or signing certificate is null"); - } - ContentVerifierProvider contentVerifierProvider; - try { - contentVerifierProvider = - new JcaContentVerifierProviderBuilder().setProvider("BC").build(signingKey); - return cert.isSignatureValid(contentVerifierProvider); - } catch (OperatorCreationException | CertException e) { - log.info("Exception thrown while verifying certificate", e); - log.info(String.format("%s.isSignatureValid(%s)", cert.getSerialNumber(), - signingKey.getFormat())); - return false; - } - } - - /** - * Checks whether given X.509 public-key certificate is self-signed. If the cert can be - * verified using its own public key, that means it was self-signed. - * - * @param cert - * X.509 Certificate - * @return boolean indicating if the cert was self-signed - */ - private static boolean isSelfSigned(final X509Certificate cert) - throws SupplyChainValidatorException { - if (cert == null) { - throw new SupplyChainValidatorException("Certificate is null"); - } - try { - PublicKey key = cert.getPublicKey(); - cert.verify(key); - return true; - } catch (SignatureException | InvalidKeyException e) { - return false; - } catch (CertificateException | NoSuchAlgorithmException | NoSuchProviderException e) { - log.error("Exception occurred while checking if cert is self-signed", e); - return false; - } - } -} diff --git a/HIRS_Utils/src/main/java/hirs/utils/tpm/eventlog/uefi/UefiGuid.java b/HIRS_Utils/src/main/java/hirs/utils/tpm/eventlog/uefi/UefiGuid.java index 7ceecdc0..aa83f937 100644 --- a/HIRS_Utils/src/main/java/hirs/utils/tpm/eventlog/uefi/UefiGuid.java +++ b/HIRS_Utils/src/main/java/hirs/utils/tpm/eventlog/uefi/UefiGuid.java @@ -23,7 +23,7 @@ public class UefiGuid { */ private static final int UUID_EPOCH_DIVISOR = 10000; - private static final Path JSON_PATH = FileSystems.getDefault().getPath("/opt", + private static final Path JSON_PATH = FileSystems.getDefault().getPath("/etc", "hirs/aca", "default-properties", "vendor-table.json"); private JsonObject uefiVendorRef; /** From 310102bc8addfc9dcf4055f1c35ccb90ac8306aa Mon Sep 17 00:00:00 2001 From: Cyrus <24922493+cyrus-dev@users.noreply.github.com> Date: Thu, 21 Sep 2023 16:13:31 -0400 Subject: [PATCH 02/15] Missed files --- .../persist/service/ValidationManager.java | 338 +++++ .../CertificateAttributeScvValidator.java | 1125 +++++++++++++++++ .../validation/FirmwareScvValidator.java | 258 ++++ 3 files changed, 1721 insertions(+) create mode 100644 HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/service/ValidationManager.java create mode 100644 HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/validation/CertificateAttributeScvValidator.java create mode 100644 HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/validation/FirmwareScvValidator.java diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/service/ValidationManager.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/service/ValidationManager.java new file mode 100644 index 00000000..f1dbaeee --- /dev/null +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/service/ValidationManager.java @@ -0,0 +1,338 @@ +package hirs.attestationca.persist.service; + +import hirs.attestationca.persist.entity.ArchivableEntity; +import hirs.attestationca.persist.entity.manager.CACredentialRepository; +import hirs.attestationca.persist.entity.manager.CertificateRepository; +import hirs.attestationca.persist.entity.manager.ComponentResultRepository; +import hirs.attestationca.persist.entity.manager.ReferenceDigestValueRepository; +import hirs.attestationca.persist.entity.manager.ReferenceManifestRepository; +import hirs.attestationca.persist.entity.userdefined.Certificate; +import hirs.attestationca.persist.entity.userdefined.Device; +import hirs.attestationca.persist.entity.userdefined.PolicySettings; +import hirs.attestationca.persist.entity.userdefined.SupplyChainValidation; +import hirs.attestationca.persist.entity.userdefined.certificate.CertificateAuthorityCredential; +import hirs.attestationca.persist.entity.userdefined.certificate.ComponentResult; +import hirs.attestationca.persist.entity.userdefined.certificate.EndorsementCredential; +import hirs.attestationca.persist.entity.userdefined.certificate.PlatformCredential; +import hirs.attestationca.persist.entity.userdefined.report.DeviceInfoReport; +import hirs.attestationca.persist.enums.AppraisalStatus; +import hirs.attestationca.persist.validation.CertificateAttributeScvValidator; +import hirs.attestationca.persist.validation.CredentialValidator; +import hirs.attestationca.persist.validation.FirmwareScvValidator; +import hirs.utils.BouncyCastleUtils; +import lombok.extern.log4j.Log4j2; +import org.apache.logging.log4j.Level; +import org.bouncycastle.util.encoders.Hex; + +import java.io.IOException; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +@Log4j2 +public class ValidationManager { + + public static SupplyChainValidation evaluateEndorsementCredentialStatus( + final EndorsementCredential ec, + final CACredentialRepository caCredentialRepository, + final boolean acceptExpiredCerts) { + final SupplyChainValidation.ValidationType validationType + = SupplyChainValidation.ValidationType.ENDORSEMENT_CREDENTIAL; + log.info("Validating endorsement credential"); + if (ec == null) { + log.error("No endorsement credential to validate"); + return buildValidationRecord(validationType, + AppraisalStatus.Status.FAIL, "Endorsement credential is missing", + null, Level.ERROR); + } + + KeyStore ecStore = getCaChain(ec, caCredentialRepository); + AppraisalStatus result = CredentialValidator. + validateEndorsementCredential(ec, ecStore, acceptExpiredCerts); + switch (result.getAppStatus()) { + case PASS: + return buildValidationRecord(validationType, AppraisalStatus.Status.PASS, + result.getMessage(), ec, Level.INFO); + case FAIL: + return buildValidationRecord(validationType, AppraisalStatus.Status.FAIL, + result.getMessage(), ec, Level.WARN); + case ERROR: + default: + return buildValidationRecord(validationType, AppraisalStatus.Status.ERROR, + result.getMessage(), ec, Level.ERROR); + } + } + + public static SupplyChainValidation evaluatePlatformCredentialStatus( + final PlatformCredential pc, + final KeyStore trustedCertificateAuthority, final boolean acceptExpiredCerts) { + final SupplyChainValidation.ValidationType validationType + = SupplyChainValidation.ValidationType.PLATFORM_CREDENTIAL; + + if (pc == null) { + log.error("No platform credential to validate"); + return buildValidationRecord(validationType, + AppraisalStatus.Status.FAIL, "Empty Platform credential", null, Level.ERROR); + } + log.info("Validating Platform Credential"); + AppraisalStatus result = CredentialValidator.validatePlatformCredential(pc, + trustedCertificateAuthority, acceptExpiredCerts); + switch (result.getAppStatus()) { + case PASS: + return buildValidationRecord(validationType, AppraisalStatus.Status.PASS, + result.getMessage(), pc, Level.INFO); + case FAIL: + return buildValidationRecord(validationType, AppraisalStatus.Status.FAIL, + result.getMessage(), pc, Level.WARN); + case ERROR: + default: + return buildValidationRecord(validationType, AppraisalStatus.Status.ERROR, + result.getMessage(), pc, Level.ERROR); + } + } + + public static SupplyChainValidation evaluatePCAttributesStatus( + final PlatformCredential pc, final DeviceInfoReport deviceInfoReport, + final EndorsementCredential ec, + final CertificateRepository certificateRepository, + final ComponentResultRepository componentResultRepository) { + final SupplyChainValidation.ValidationType validationType + = SupplyChainValidation.ValidationType.PLATFORM_CREDENTIAL_ATTRIBUTES; + + if (pc == null) { + log.error("No platform credential to validate"); + return buildValidationRecord(validationType, + AppraisalStatus.Status.FAIL, "Platform credential is missing", + null, Level.ERROR); + } + log.info("Validating platform credential attributes"); + AppraisalStatus result = CredentialValidator. + validatePlatformCredentialAttributes(pc, deviceInfoReport, ec); + switch (result.getAppStatus()) { + case PASS: + return buildValidationRecord(validationType, AppraisalStatus.Status.PASS, + result.getMessage(), pc, Level.INFO); + case FAIL: + if (!result.getAdditionalInfo().isEmpty()) { + pc.setComponentFailures(result.getAdditionalInfo()); + pc.setComponentFailureMessage(result.getMessage()); + certificateRepository.save(pc); + for (ComponentResult componentResult + : CertificateAttributeScvValidator.getComponentResultList()) { + componentResultRepository.save(componentResult); + } + } + return buildValidationRecord(validationType, AppraisalStatus.Status.FAIL, + result.getMessage(), pc, Level.WARN); + case ERROR: + default: + return buildValidationRecord(validationType, AppraisalStatus.Status.ERROR, + result.getMessage(), pc, Level.ERROR); + } + } + + public static SupplyChainValidation evaluateDeltaAttributesStatus( + final PlatformCredential delta, + final DeviceInfoReport deviceInfoReport, + final PlatformCredential base, + final Map deltaMapping, + final CertificateRepository certificateRepository) { + final SupplyChainValidation.ValidationType validationType + = SupplyChainValidation.ValidationType.PLATFORM_CREDENTIAL_ATTRIBUTES; + + if (delta == null) { + log.error("No delta certificate to validate"); + return buildValidationRecord(validationType, + AppraisalStatus.Status.FAIL, "Delta platform certificate is missing", + null, Level.ERROR); + } + log.info("Validating delta platform certificate attributes"); + AppraisalStatus result = CertificateAttributeScvValidator. + validateDeltaPlatformCredentialAttributes(delta, deviceInfoReport, + base, deltaMapping); + switch (result.getAppStatus()) { + case PASS: + return buildValidationRecord(validationType, AppraisalStatus.Status.PASS, + result.getMessage(), delta, Level.INFO); + case FAIL: + if (!result.getAdditionalInfo().isEmpty()) { + base.setComponentFailures(result.getAdditionalInfo()); + base.setComponentFailureMessage(result.getMessage()); + certificateRepository.save(base); + } + // we are adding things to componentFailures + certificateRepository.save(delta); + return buildValidationRecord(validationType, AppraisalStatus.Status.FAIL, + result.getMessage(), delta, Level.WARN); + case ERROR: + default: + return buildValidationRecord(validationType, AppraisalStatus.Status.ERROR, + result.getMessage(), delta, Level.ERROR); + } + } + + public static SupplyChainValidation evaluateFirmwareStatus( + final Device device, + final PolicySettings policySettings, final ReferenceManifestRepository rimRepo, + final ReferenceDigestValueRepository rdvRepo, + final CACredentialRepository caRepo) { + final SupplyChainValidation.ValidationType validationType + = SupplyChainValidation.ValidationType.FIRMWARE; + + AppraisalStatus result = FirmwareScvValidator.validateFirmware(device, policySettings, + rimRepo, rdvRepo, caRepo); + Level logLevel; + + switch (result.getAppStatus()) { + case PASS: + logLevel = Level.INFO; + break; + case FAIL: + logLevel = Level.WARN; + break; + case ERROR: + default: + logLevel = Level.ERROR; + } + return buildValidationRecord(validationType, result.getAppStatus(), + result.getMessage(), null, logLevel); + } + + /** + * Creates a supply chain validation record and logs the validation message + * at the specified log level. + * + * @param validationType the type of validation + * @param result the appraisal status + * @param message the validation message to include in the summary and log + * @param archivableEntity the archivableEntity associated with the + * validation + * @param logLevel the log level + * @return a SupplyChainValidation + */ + public static SupplyChainValidation buildValidationRecord( + final SupplyChainValidation.ValidationType validationType, + final AppraisalStatus.Status result, final String message, + final ArchivableEntity archivableEntity, final Level logLevel) { + List aeList = new ArrayList<>(); + if (archivableEntity != null) { + aeList.add(archivableEntity); + } + + log.log(logLevel, message); + return new SupplyChainValidation(validationType, result, aeList, message); + } + + /** + * This method is used to retrieve the entire CA chain (up to a trusted + * self-signed certificate) for the given certificate. This method will look + * up CA certificates that have a matching issuer organization as the given + * certificate, and will perform that operation recursively until all + * certificates for all relevant organizations have been retrieved. For that + * reason, the returned set of certificates may be larger than the the + * single trust chain for the queried certificate, but is guaranteed to + * include the trust chain if it exists in this class' CertificateManager. + * Returns the certificate authority credentials in a KeyStore. + * + * @param certificate the credential whose CA chain should be retrieved + * @param caCredentialRepository db service to get CA Certs + * @return A keystore containing all relevant CA credentials to the given + * certificate's organization or null if the keystore can't be assembled + */ + public static KeyStore getCaChain(final Certificate certificate, + final CACredentialRepository caCredentialRepository) { + KeyStore caKeyStore = null; + try { + caKeyStore = caCertSetToKeystore(getCaChainRec(certificate, Collections.emptySet(), + caCredentialRepository)); + } catch (KeyStoreException | IOException e) { + log.error("Unable to assemble CA keystore", e); + } + return caKeyStore; + } + + /** + * This is a recursive method which is used to retrieve the entire CA chain + * (up to a trusted self-signed certificate) for the given certificate. This + * method will look up CA certificates that have a matching issuer + * organization as the given certificate, and will perform that operation + * recursively until all certificates for all relevant organizations have + * been retrieved. For that reason, the returned set of certificates may be + * larger than the the single trust chain for the queried certificate, but + * is guaranteed to include the trust chain if it exists in this class' + * CertificateManager. + *

+ * Implementation notes: 1. Queries for CA certs with a subject org matching + * the given (argument's) issuer org 2. Add that org to + * queriedOrganizations, so we don't search for that organization again 3. + * For each returned CA cert, add that cert to the result set, and recurse + * with that as the argument (to go up the chain), if and only if we haven't + * 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 previouslyQueriedSubjects a list of organizations to refrain + * from querying + * @return a Set containing all relevant CA credentials to the given + * certificate's organization + */ + public static Set getCaChainRec( + final Certificate credential, + final Set previouslyQueriedSubjects, + final CACredentialRepository caCredentialRepository) { + CertificateAuthorityCredential skiCA = null; + List certAuthsWithMatchingIssuer = new LinkedList<>(); + if (credential.getAuthorityKeyIdentifier() != null + && !credential.getAuthorityKeyIdentifier().isEmpty()) { + byte[] bytes = Hex.decode(credential.getAuthorityKeyIdentifier()); + skiCA = caCredentialRepository.findBySubjectKeyIdentifier(bytes); + } + + if (skiCA == null) { + if (credential.getIssuerSorted() == null + || credential.getIssuerSorted().isEmpty()) { + certAuthsWithMatchingIssuer = caCredentialRepository.findBySubject(credential.getIssuer()); + } else { + //Get certificates by subject organization + certAuthsWithMatchingIssuer = caCredentialRepository.findBySubjectSorted(credential.getIssuerSorted()); + } + } else { + certAuthsWithMatchingIssuer.add(skiCA); + } + Set queriedOrganizations = new HashSet<>(previouslyQueriedSubjects); + queriedOrganizations.add(credential.getIssuer()); + + HashSet caCreds = new HashSet<>(); + for (CertificateAuthorityCredential cred : certAuthsWithMatchingIssuer) { + caCreds.add(cred); + if (!BouncyCastleUtils.x500NameCompare(cred.getIssuer(), + cred.getSubject())) { + caCreds.addAll(getCaChainRec(cred, queriedOrganizations, caCredentialRepository)); + } + } + return caCreds; + } + + public static KeyStore caCertSetToKeystore(final Set certs) + throws KeyStoreException, IOException { + KeyStore keyStore = KeyStore.getInstance("JKS"); + try { + keyStore.load(null, "".toCharArray()); + for (Certificate cert : certs) { + keyStore.setCertificateEntry(cert.getId().toString(), cert.getX509Certificate()); + } + } catch (IOException | CertificateException | NoSuchAlgorithmException e) { + throw new IOException("Could not create and populate keystore", e); + } + + return keyStore; + } +} diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/validation/CertificateAttributeScvValidator.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/validation/CertificateAttributeScvValidator.java new file mode 100644 index 00000000..5140632a --- /dev/null +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/validation/CertificateAttributeScvValidator.java @@ -0,0 +1,1125 @@ +package hirs.attestationca.persist.validation; + +import hirs.attestationca.persist.entity.ArchivableEntity; +import hirs.attestationca.persist.entity.userdefined.SupplyChainValidation; +import hirs.attestationca.persist.entity.userdefined.certificate.ComponentResult; +import hirs.attestationca.persist.entity.userdefined.certificate.PlatformCredential; +import hirs.attestationca.persist.entity.userdefined.certificate.attributes.ComponentIdentifier; +import hirs.attestationca.persist.entity.userdefined.certificate.attributes.V2.ComponentIdentifierV2; +import hirs.attestationca.persist.entity.userdefined.info.ComponentInfo; +import hirs.attestationca.persist.entity.userdefined.info.HardwareInfo; +import hirs.attestationca.persist.entity.userdefined.report.DeviceInfoReport; +import hirs.attestationca.persist.enums.AppraisalStatus; +import hirs.attestationca.persist.util.PciIds; +import hirs.utils.enums.DeviceInfoEnums; +import lombok.Getter; +import lombok.Setter; +import lombok.extern.log4j.Log4j2; +import org.apache.commons.codec.digest.DigestUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.logging.log4j.util.Strings; +import org.bouncycastle.asn1.DERUTF8String; + +import java.io.IOException; +import java.math.BigInteger; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.UUID; +import java.util.stream.Collectors; + +import static hirs.attestationca.persist.enums.AppraisalStatus.Status.ERROR; +import static hirs.attestationca.persist.enums.AppraisalStatus.Status.FAIL; +import static hirs.attestationca.persist.enums.AppraisalStatus.Status.PASS; + +@Log4j2 +public class CertificateAttributeScvValidator extends SupplyChainCredentialValidator { + + @Setter + @Getter + private static List componentResultList = new LinkedList<>(); + + /** + * Checks if the delta credential's attributes are valid. + * @param deltaPlatformCredential the delta credential to verify + * @param deviceInfoReport The device info report containing + * serial number of the platform to be validated. + * @param basePlatformCredential the base credential from the same identity request + * as the delta credential. + * @param deltaMapping delta certificates associated with the + * delta supply validation. + * @return the result of the validation. + */ + public static AppraisalStatus validateDeltaPlatformCredentialAttributes( + final PlatformCredential deltaPlatformCredential, + final DeviceInfoReport deviceInfoReport, + final PlatformCredential basePlatformCredential, + final Map deltaMapping) { + String 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 pc : deltaMapping.keySet()) { + if (!basePlatformCredential.getPlatformSerial() + .equals(pc.getPlatformSerial())) { + message = String.format("Base and Delta platform serial " + + "numbers do not match (%s != %s)", + pc.getPlatformSerial(), + basePlatformCredential.getPlatformSerial()); + log.error(message); + return new AppraisalStatus(FAIL, message); + } + // none of the deltas should have the serial number of the base + if (!pc.isPlatformBase() && basePlatformCredential.getSerialNumber() + .equals(pc.getSerialNumber())) { + message = String.format("Delta Certificate with same serial number as base. (%s)", + pc.getSerialNumber()); + log.error(message); + return new AppraisalStatus(FAIL, message); + } + } + + // parse out the provided delta and its specific chain. + List origPcComponents + = new LinkedList<>(basePlatformCredential.getComponentIdentifiers()); + + return validateDeltaAttributesChainV2p0(deltaPlatformCredential.getId(), + deviceInfoReport, deltaMapping, origPcComponents); + } + + public static AppraisalStatus validatePlatformCredentialAttributesV1p2( + final PlatformCredential platformCredential, + final DeviceInfoReport deviceInfoReport) { + + // check the device's board serial number, and compare against this + // platform credential's board serial number. + // Retrieve the various device serial numbers. + String credentialBoardSerialNumber = platformCredential.getPlatformSerial(); + String credentialChassisSerialNumber = platformCredential.getChassisSerialNumber(); + + HardwareInfo hardwareInfo = deviceInfoReport.getHardwareInfo(); + String deviceBaseboardSerialNumber = hardwareInfo.getBaseboardSerialNumber(); + String deviceChassisSerialNumber = hardwareInfo.getChassisSerialNumber(); + String deviceSystemSerialNumber = hardwareInfo.getSystemSerialNumber(); + + // log serial numbers that weren't collected. Force "not specified" serial numbers + // to be ignored in below case checks + Map deviceInfoSerialNumbers = new HashMap<>(); + + if (StringUtils.isEmpty(deviceBaseboardSerialNumber) + || DeviceInfoEnums.NOT_SPECIFIED.equalsIgnoreCase(deviceBaseboardSerialNumber)) { + log.error("Failed to retrieve device baseboard serial number"); + deviceBaseboardSerialNumber = null; + } else { + deviceInfoSerialNumbers.put("board serial number", deviceBaseboardSerialNumber); + log.info("Using device board serial number for validation: " + + deviceBaseboardSerialNumber); + } + + if (StringUtils.isEmpty(deviceChassisSerialNumber) + || DeviceInfoEnums.NOT_SPECIFIED.equalsIgnoreCase(deviceChassisSerialNumber)) { + log.error("Failed to retrieve device chassis serial number"); + } else { + deviceInfoSerialNumbers.put("chassis serial number", deviceChassisSerialNumber); + log.info("Using device chassis serial number for validation: " + + deviceChassisSerialNumber); + } + if (StringUtils.isEmpty(deviceSystemSerialNumber) + || DeviceInfoEnums.NOT_SPECIFIED.equalsIgnoreCase(deviceSystemSerialNumber)) { + log.error("Failed to retrieve device system serial number"); + } else { + deviceInfoSerialNumbers.put("system serial number", deviceSystemSerialNumber); + log.info("Using device system serial number for validation: " + + deviceSystemSerialNumber); + } + + AppraisalStatus status; + + // Test 1: If the board serial number or chassis is set on the PC, + // compare with each of the device serial numbers for any match + if (StringUtils.isNotEmpty(credentialBoardSerialNumber) + || StringUtils.isNotEmpty(credentialChassisSerialNumber)) { + status = validatePlatformSerialsWithDeviceSerials(credentialBoardSerialNumber, + credentialChassisSerialNumber, deviceInfoSerialNumbers); + // Test 2: If the board and chassis serial numbers are not set on the PC, + // compare the SHA1 hash of the device baseboard serial number to + // the certificate serial number + } else { + String message; + log.debug("Credential Serial Number was null"); + if (StringUtils.isEmpty(deviceBaseboardSerialNumber)) { + message = "Device Serial Number was null"; + log.error(message); + status = new AppraisalStatus(FAIL, message); + } else { + // Calculate the SHA1 hash of the UTF8 encoded baseboard serial number + BigInteger baseboardSha1 = new BigInteger(1, + DigestUtils.sha1(deviceBaseboardSerialNumber.getBytes(StandardCharsets.UTF_8))); + BigInteger certificateSerialNumber = platformCredential.getSerialNumber(); + + // compare the SHA1 hash of the baseboard serial number to the certificate SN + if (certificateSerialNumber != null + && certificateSerialNumber.equals(baseboardSha1)) { + log.info("Device Baseboard Serial Number matches " + + "the Certificate Serial Number"); + status = new AppraisalStatus(PASS, PLATFORM_ATTRIBUTES_VALID); + } else if (certificateSerialNumber != null + && certificateSerialNumber.equals( + baseboardSha1.clearBit(NUC_VARIABLE_BIT))) { + log.info("Warning! The Certificate serial number had the most significant " + + "bit truncated. 159 bits of it matched the device baseboard " + + "serial number."); + status = new AppraisalStatus(PASS, PLATFORM_ATTRIBUTES_VALID); + } else { + message = "The SHA1 hash of the Device Baseboard Serial Number " + + deviceBaseboardSerialNumber + + " did not match the Certificate's Serial Number"; + log.error(message); + status = new AppraisalStatus(FAIL, message); + + } + } + } + + return status; + } + + + /** + * Validates device info report against the new platform credential. + * @param platformCredential the Platform Credential + * @param deviceInfoReport the Device Info Report + * @return either PASS or FAIL + */ + public static AppraisalStatus validatePlatformCredentialAttributesV2p0( + final PlatformCredential platformCredential, + final DeviceInfoReport deviceInfoReport) { + boolean passesValidation = true; + StringBuilder resultMessage = new StringBuilder(); + + HardwareInfo hardwareInfo = deviceInfoReport.getHardwareInfo(); + + boolean fieldValidation; + fieldValidation = requiredPlatformCredentialFieldIsNonEmptyAndMatches( + "PlatformManufacturerStr", + platformCredential.getManufacturer(), + hardwareInfo.getManufacturer()); + + if (!fieldValidation) { + resultMessage.append("Platform manufacturer did not match\n"); + } + + passesValidation &= fieldValidation; + + fieldValidation = requiredPlatformCredentialFieldIsNonEmptyAndMatches( + "PlatformModel", + platformCredential.getModel(), + hardwareInfo.getProductName()); + + if (!fieldValidation) { + resultMessage.append("Platform model did not match\n"); + } + + passesValidation &= fieldValidation; + + fieldValidation = requiredPlatformCredentialFieldIsNonEmptyAndMatches( + "PlatformVersion", + platformCredential.getVersion(), + hardwareInfo.getVersion()); + + if (!fieldValidation) { + resultMessage.append("Platform version did not match\n"); + } + + passesValidation &= fieldValidation; + + // check PlatformSerial against both system-serial-number and baseboard-serial-number + fieldValidation = ( + (optionalPlatformCredentialFieldNullOrMatches( + "PlatformSerial", + platformCredential.getPlatformSerial(), + hardwareInfo.getSystemSerialNumber())) + || (optionalPlatformCredentialFieldNullOrMatches( + "PlatformSerial", + platformCredential.getPlatformSerial(), + hardwareInfo.getBaseboardSerialNumber()))); + + if (!fieldValidation) { + resultMessage.append("Platform serial did not match\n"); + } + + passesValidation &= fieldValidation; + + // Retrieve the list of all components from the Platform Credential + List allPcComponents + = new ArrayList<>(platformCredential.getComponentIdentifiers()); + + // All components listed in the Platform Credential must have a manufacturer and model + for (ComponentIdentifier pcComponent : allPcComponents) { + fieldValidation = !hasEmptyValueForRequiredField("componentManufacturer", + pcComponent.getComponentManufacturer()); + + if (!fieldValidation) { + resultMessage.append("Component manufacturer is empty\n"); + } + + passesValidation &= fieldValidation; + + fieldValidation = !hasEmptyValueForRequiredField("componentModel", + pcComponent.getComponentModel()); + + if (!fieldValidation) { + resultMessage.append("Component model is empty\n"); + } + + passesValidation &= fieldValidation; + } + + // There is no need to do comparisons with components that are invalid because + // they did not have a manufacturer or model. + List validPcComponents = allPcComponents.stream() + .filter(identifier -> identifier.getComponentManufacturer() != null + && identifier.getComponentModel() != null) + .collect(Collectors.toList()); + + String paccorOutputString = deviceInfoReport.getPaccorOutputString(); + String unmatchedComponents; + try { + List componentInfoList + = getComponentInfoFromPaccorOutput(paccorOutputString); + unmatchedComponents = validateV2p0PlatformCredentialComponentsExpectingExactMatch( + platformCredential.getId(), + validPcComponents, componentInfoList); + fieldValidation &= unmatchedComponents.isEmpty(); + } catch (IOException e) { + final String baseErrorMessage = "Error parsing JSON output from PACCOR: "; + log.error(baseErrorMessage + e.toString()); + log.error("PACCOR output string:\n" + paccorOutputString); + 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 + int counter = 0; + for (ComponentIdentifier ci : validPcComponents) { + counter++; + additionalInfo.append(String.format("%d;", ci.hashCode())); + } + if (counter > 0) { + additionalInfo.insert(0, "COMPID="); + additionalInfo.append(counter); + } + } + + passesValidation &= fieldValidation; + + if (passesValidation) { + return new AppraisalStatus(PASS, PLATFORM_ATTRIBUTES_VALID); + } else { + return new AppraisalStatus(FAIL, resultMessage.toString(), additionalInfo.toString()); + } + } + + /** + * The main purpose of this method, the in process of validation, is to + * pick out the changes that lead to the delta cert and make sure the changes + * are valid. + * + * @param deviceInfoReport The paccor profile of device being validated against. + * @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. + */ + @SuppressWarnings("methodlength") + static AppraisalStatus validateDeltaAttributesChainV2p0( + final UUID certificateId, + final DeviceInfoReport deviceInfoReport, + final Map deltaMapping, + final List origPcComponents) { + boolean fieldValidation = true; + StringBuilder resultMessage = new StringBuilder(); + String tempStringMessage = ""; + List validOrigPcComponents = origPcComponents.stream() + .filter(identifier -> identifier.getComponentManufacturer() != null + && identifier.getComponentModel() != null) + .collect(Collectors.toList()); + List chainCertificates = new LinkedList<>(deltaMapping.keySet()); + + // map the components throughout the chain + List baseCompList = new LinkedList<>(validOrigPcComponents); + + Collections.sort(chainCertificates, new Comparator() { + @Override + public int compare(final PlatformCredential obj1, + final PlatformCredential obj2) { + if (obj1 == null) { + return 0; + } + if (obj2 == null) { + return 0; + } + if (obj1.getBeginValidity() == null || obj2.getBeginValidity() == null) { + return 0; + } + return obj1.getBeginValidity().compareTo(obj2.getBeginValidity()); + } + }); + // start of some changes + resultMessage.append("There are errors with Delta " + + "Component Statuses:\n"); + List leftOverDeltas = new ArrayList<>(); + List absentSerialNum = new ArrayList<>(); + tempStringMessage = validateDeltaChain(deltaMapping, baseCompList, + leftOverDeltas, absentSerialNum, chainCertificates); + + // check if there were any issues + if (!tempStringMessage.isEmpty()) { + resultMessage.append(tempStringMessage); + fieldValidation = false; + } + + // finished up + List certificateList = null; + SupplyChainValidation scv = null; + StringBuilder deltaSb = new StringBuilder(); + + // 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().getComponentIdentifier(); + baseCiV2 = (ComponentIdentifierV2) ci; + classFound = classValue.equals(baseCiV2.getComponentClass() + .getComponentIdentifier()); + if (classFound) { + if (isMatch(ciV2, baseCiV2)) { + if (ciV2.isAdded() || ciV2.isModified()) { + // since the base list doesn't have this ci + // just add the delta + baseCompList.add(deltaCi); + break; + } + if (ciV2.isRemoved()) { + baseCompList.remove(ciV2); + break; + } + // 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())); + } + } + } + } + + if (!fieldValidation || !deltaSb.toString().isEmpty()) { + deltaSb.insert(0, "COMPID="); + return new AppraisalStatus(FAIL, resultMessage.toString(), deltaSb.toString()); + } + + String paccorOutputString = deviceInfoReport.getPaccorOutputString(); + String unmatchedComponents; + try { + // compare based on component class + List componentInfoList = getV2PaccorOutput(paccorOutputString); + // this is what I want to rewrite + unmatchedComponents = validateV2PlatformCredentialAttributes( + certificateId, + baseCompList, + componentInfoList); + fieldValidation &= unmatchedComponents.isEmpty(); + } catch (IOException ioEx) { + final String baseErrorMessage = "Error parsing JSON output from PACCOR: "; + log.error(baseErrorMessage + ioEx.toString()); + log.error("PACCOR output string:\n" + paccorOutputString); + return new AppraisalStatus(ERROR, baseErrorMessage + ioEx.getMessage()); + } + StringBuilder additionalInfo = new StringBuilder(); + if (!fieldValidation) { + resultMessage = new StringBuilder(); + resultMessage.append("There are unmatched components:\n"); + resultMessage.append(unmatchedComponents); + + // pass information of which ones failed in additionInfo + int counter = 0; + for (ComponentIdentifier ci : baseCompList) { + counter++; + additionalInfo.append(String.format("%d;", ci.hashCode())); + } + if (counter > 0) { + additionalInfo.insert(0, "COMPID="); + additionalInfo.append(counter); + } + } + + if (fieldValidation) { + return new AppraisalStatus(PASS, PLATFORM_ATTRIBUTES_VALID); + } else { + return new AppraisalStatus(FAIL, resultMessage.toString(), additionalInfo.toString()); + } + } + + private static String validateV2PlatformCredentialAttributes( + final UUID certificateId, + final List fullDeltaChainComponents, + final List allDeviceInfoComponents) { + ComponentIdentifierV2 ciV2; + StringBuilder invalidPcIds = new StringBuilder(); + List subCompIdList = fullDeltaChainComponents + .stream().collect(Collectors.toList()); + List subCompInfoList = allDeviceInfoComponents + .stream().collect(Collectors.toList()); + + // Delta is the baseline + for (ComponentInfo cInfo : allDeviceInfoComponents) { + for (ComponentIdentifier cId : fullDeltaChainComponents) { + ciV2 = (ComponentIdentifierV2) cId; + if (cInfo.getComponentClass().contains( + ciV2.getComponentClass().getComponentIdentifier()) + && isMatch(certificateId, cId, cInfo)) { + subCompIdList.remove(cId); + subCompInfoList.remove(cInfo); + } + } + } + + 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 + fullDeltaChainComponents.clear(); + for (ComponentIdentifier ci : subCompIdList) { + if (ci.isVersion2() && PciIds.DB.isReady()) { + ci = PciIds.translate((ComponentIdentifierV2) ci); + } + log.error("Unmatched component: " + ci); + fullDeltaChainComponents.add(ci); + invalidPcIds.append(String.format( + "Manufacturer=%s, Model=%s, Serial=%s, Revision=%s;%n", + ci.getComponentManufacturer(), + ci.getComponentModel(), + ci.getComponentSerial(), + ci.getComponentRevision())); + } + } + + return invalidPcIds.toString(); + } + + private static String validateDeltaChain( + final Map deltaMapping, + final List baseCompList, + final List leftOvers, + final List absentSerials, + final List chainCertificates) { + StringBuilder resultMessage = new StringBuilder(); + List 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 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 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.getValidationResult() != PASS) { + failureMsg.append(scv.getMessage()); + } + deltaMapping.put(delta, new SupplyChainValidation( + SupplyChainValidation.ValidationType.PLATFORM_CREDENTIAL, + 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.getValidationResult() != PASS) { + failureMsg.append(scv.getMessage()); + } + deltaMapping.put(delta, new SupplyChainValidation( + SupplyChainValidation.ValidationType.PLATFORM_CREDENTIAL, + 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.getValidationResult() != PASS) { + failureMsg.append(scv.getMessage()); + } + deltaMapping.put(delta, new SupplyChainValidation( + SupplyChainValidation.ValidationType.PLATFORM_CREDENTIAL, + FAIL, + certificateList, + failureMsg.toString())); + } else { + // have to add in case later it is removed + chainCiMapping.put(ciSerial, ci); + } + } + } + } else { + if (ci.isVersion2() && ((ComponentIdentifierV2) ci).isModified()) { + ComponentIdentifierV2 ciV2 = (ComponentIdentifierV2) ci; + // Look for singular component of same make/model/class + ComponentIdentifier candidate = null; + for (ComponentIdentifier search : absentSerials) { + if (!search.isVersion2()) { + continue; + } + ComponentIdentifierV2 noSerialV2 = (ComponentIdentifierV2) search; + + if (noSerialV2.getComponentClass().getComponentIdentifier().equals( + ciV2.getComponentClass().getComponentIdentifier()) + && isMatch(noSerialV2, ciV2)) { + if (candidate == null) { + candidate = noSerialV2; + } else { + // This only works if there is one matching component + candidate = null; + break; + } + } + } + + if (candidate != null) { + absentSerials.remove(candidate); + absentSerials.add(ciV2); + } else { + leftOvers.add(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(); + } + + /** + * Compares the component information from the device info report against those of the + * platform credential. All components in the platform credential should exactly match one + * component in the device info report. The device info report is allowed to have extra + * 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 + */ + private static String validateV2p0PlatformCredentialComponentsExpectingExactMatch( + final UUID certificateId, + final List untrimmedPcComponents, + final List allDeviceInfoComponents) { + // For each manufacturer listed in the platform credential, create two lists: + // 1. a list of components listed in the platform credential for the manufacturer, and + // 2. a list of components listed in the device info for the same manufacturer + // Then eliminate matches from both lists. Finally, decide if the validation passes based + // on the leftovers in the lists and the policy in place. + final List pcComponents = new ArrayList<>(); + for (ComponentIdentifier component : untrimmedPcComponents) { + if (component.getComponentManufacturer() != null) { + component.setComponentManufacturer(new DERUTF8String( + component.getComponentManufacturer().getString().trim())); + } + if (component.getComponentModel() != null) { + component.setComponentModel(new DERUTF8String( + component.getComponentModel().getString().trim())); + } + if (component.getComponentSerial() != null) { + component.setComponentSerial(new DERUTF8String( + component.getComponentSerial().getString().trim())); + } + if (component.getComponentRevision() != null) { + component.setComponentRevision(new DERUTF8String( + component.getComponentRevision().getString().trim())); + } + pcComponents.add(component); + } + + log.info("Validating the following Platform Cert components..."); + pcComponents.forEach(component -> log.info(component.toString())); + log.info("...against the the following DeviceInfoReport components:"); + allDeviceInfoComponents.forEach(component -> log.info(component.toString())); + Set manufacturerSet = new HashSet<>(); + pcComponents.forEach(pcComp -> manufacturerSet.add(pcComp.getComponentManufacturer())); + + // Create a list for unmatched components across all manufacturers to display at the end. + List pcUnmatchedComponents = new ArrayList<>(); + + for (DERUTF8String derUtf8Manufacturer : manufacturerSet) { + List pcComponentsFromManufacturer + = pcComponents.stream().filter(compIdentifier + -> compIdentifier.getComponentManufacturer().equals(derUtf8Manufacturer)) + .collect(Collectors.toList()); + + String pcManufacturer = derUtf8Manufacturer.getString(); + List deviceInfoComponentsFromManufacturer + = allDeviceInfoComponents.stream().filter(componentInfo + -> componentInfo.getComponentManufacturer().equals(pcManufacturer)) + .collect(Collectors.toList()); + // For each component listed in the platform credential from this manufacturer + // find the ones that specify a serial number so we can match the most specific ones + // first. + List pcComponentsFromManufacturerWithSerialNumber + = pcComponentsFromManufacturer.stream().filter(compIdentifier + -> compIdentifier.getComponentSerial() != null + && StringUtils.isNotEmpty(compIdentifier.getComponentSerial().getString())) + .collect(Collectors.toList()); + // Now match up the components from the device info that are from the same + // manufacturer and have a serial number. As matches are found, remove them from + // both lists. + for (ComponentIdentifier pcComponent + : pcComponentsFromManufacturerWithSerialNumber) { + Optional first + = deviceInfoComponentsFromManufacturer.stream() + .filter(componentInfo + -> StringUtils.isNotEmpty(componentInfo.getComponentSerial())) + .filter(componentInfo -> componentInfo.getComponentSerial() + .equals(pcComponent.getComponentSerial().getString())).findFirst(); + + if (first.isPresent()) { + ComponentInfo potentialMatch = first.get(); + if (isMatch(certificateId, pcComponent, potentialMatch)) { + pcComponentsFromManufacturer.remove(pcComponent); + deviceInfoComponentsFromManufacturer.remove(potentialMatch); + } + } + } + // For each component listed in the platform credential from this manufacturer + // find the ones that specify value for the revision field so we can match the most + // specific ones first. + List pcComponentsFromManufacturerWithRevision + = pcComponentsFromManufacturer.stream().filter(compIdentifier + -> compIdentifier.getComponentRevision() != null + && StringUtils.isNotEmpty(compIdentifier.getComponentRevision().getString())) + .collect(Collectors.toList()); + // Now match up the components from the device info that are from the same + // manufacturer and specify a value for the revision field. As matches are found, + // remove them from both lists. + for (ComponentIdentifier pcComponent + : pcComponentsFromManufacturerWithRevision) { + Optional first + = deviceInfoComponentsFromManufacturer.stream() + .filter(info -> StringUtils.isNotEmpty(info.getComponentRevision())) + .filter(info -> info.getComponentRevision() + .equals(pcComponent.getComponentRevision().getString())) + .findFirst(); + + if (first.isPresent()) { + ComponentInfo potentialMatch = first.get(); + if (isMatch(certificateId, pcComponent, potentialMatch)) { + pcComponentsFromManufacturer.remove(pcComponent); + deviceInfoComponentsFromManufacturer.remove(potentialMatch); + } + } + } + // The remaining components from the manufacturer have only the 2 required fields so + // just match them. + List templist = new ArrayList<>(pcComponentsFromManufacturer); + for (ComponentIdentifier ci : templist) { + Iterator diComponentIter + = deviceInfoComponentsFromManufacturer.iterator(); + while (diComponentIter.hasNext()) { + ComponentInfo potentialMatch = diComponentIter.next(); + if (isMatch(certificateId, ci, potentialMatch)) { + pcComponentsFromManufacturer.remove(ci); + diComponentIter.remove(); + } + } + } + pcUnmatchedComponents.addAll(pcComponentsFromManufacturer); + } + + if (!pcUnmatchedComponents.isEmpty()) { + untrimmedPcComponents.clear(); + StringBuilder sb = new StringBuilder(); + log.error(String.format("Platform Credential contained %d unmatched components:", + pcUnmatchedComponents.size())); + + int unmatchedComponentCounter = 1; + for (ComponentIdentifier unmatchedComponent : pcUnmatchedComponents) { + if (unmatchedComponent.isVersion2() && PciIds.DB.isReady()) { + unmatchedComponent = + PciIds.translate((ComponentIdentifierV2) unmatchedComponent); + } + log.error("Unmatched component " + unmatchedComponentCounter++ + ": " + + unmatchedComponent); + sb.append(String.format("Manufacturer=%s, Model=%s, Serial=%s, Revision=%s;%n", + unmatchedComponent.getComponentManufacturer(), + unmatchedComponent.getComponentModel(), + unmatchedComponent.getComponentSerial(), + unmatchedComponent.getComponentRevision())); + unmatchedComponent.setValidationResult(false); + untrimmedPcComponents.add(unmatchedComponent); + } + return sb.toString(); + } + return Strings.EMPTY; + } + + /** + * Checks if the fields in the potentialMatch match the fields in the pcComponent, + * or if the relevant field in the pcComponent is empty. + * @param certificateId the certificate id + * @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) + */ + private static boolean isMatch(final UUID certificateId, + final ComponentIdentifier pcComponent, + final ComponentInfo potentialMatch) { + boolean matchesSoFar = true; + + matchesSoFar &= isMatchOrEmptyInPlatformCert( + potentialMatch.getComponentManufacturer(), + pcComponent.getComponentManufacturer() + ); + + if (matchesSoFar) { + componentResultList.add(new ComponentResult(certificateId, pcComponent.hashCode(), + potentialMatch.getComponentSerial(), + pcComponent.getComponentSerial().getString())); + } + + matchesSoFar &= isMatchOrEmptyInPlatformCert( + potentialMatch.getComponentModel(), + pcComponent.getComponentModel() + ); + + if (matchesSoFar) { + componentResultList.add(new ComponentResult(certificateId, pcComponent.hashCode(), + potentialMatch.getComponentSerial(), + pcComponent.getComponentSerial().getString())); + } + + matchesSoFar &= isMatchOrEmptyInPlatformCert( + potentialMatch.getComponentSerial(), + pcComponent.getComponentSerial() + ); + + if (matchesSoFar) { + componentResultList.add(new ComponentResult(certificateId, pcComponent.hashCode(), + potentialMatch.getComponentSerial(), + pcComponent.getComponentSerial().getString())); + } + + matchesSoFar &= isMatchOrEmptyInPlatformCert( + potentialMatch.getComponentRevision(), + pcComponent.getComponentRevision() + ); + + if (matchesSoFar) { + componentResultList.add(new ComponentResult(certificateId, pcComponent.hashCode(), + potentialMatch.getComponentSerial(), + pcComponent.getComponentSerial().getString())); + } + + 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) + */ + private 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) { + if (valueInPlatformCert == null || StringUtils.isEmpty(valueInPlatformCert.getString())) { + return true; + } + 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. + * @param credentialBoardSerialNumber the PC board S/N + * @param credentialChassisSerialNumber the PC chassis S/N + * @param deviceInfoSerialNumbers the map of device info serial numbers with descriptions. + * @return the changed validation status + */ + private static AppraisalStatus validatePlatformSerialsWithDeviceSerials( + final String credentialBoardSerialNumber, final String credentialChassisSerialNumber, + final Map deviceInfoSerialNumbers) { + boolean boardSerialNumberFound = false; + boolean chassisSerialNumberFound = false; + + if (StringUtils.isNotEmpty(credentialBoardSerialNumber)) { + boardSerialNumberFound = deviceInfoContainsPlatformSerialNumber( + credentialBoardSerialNumber, "board serial number", deviceInfoSerialNumbers); + } + if (StringUtils.isNotEmpty(credentialChassisSerialNumber)) { + chassisSerialNumberFound = deviceInfoContainsPlatformSerialNumber( + credentialChassisSerialNumber, + "chassis serial number", deviceInfoSerialNumbers); + } + + if (boardSerialNumberFound || chassisSerialNumberFound) { + log.info("The platform credential's board or chassis serial number matched" + + " with a serial number from the client's device information"); + return new AppraisalStatus(PASS, PLATFORM_ATTRIBUTES_VALID); + } + log.error("The platform credential's board and chassis serial numbers did" + + " not match with any device info's serial numbers"); + + return new AppraisalStatus(FAIL, "Platform serial did not match device info"); + } + + /** + * Checks if a platform credential's serial number matches ANY of the device information's + * set of serial numbers. + * @param platformSerialNumber the platform serial number to compare + * @param platformSerialNumberDescription description of the serial number for logging purposes. + * @param deviceInfoSerialNumbers the map of device info serial numbers + * (key = description, value = serial number) + * @return true if the platform serial number was found (case insensitive search), + * false otherwise + */ + private static boolean deviceInfoContainsPlatformSerialNumber( + final String platformSerialNumber, final String platformSerialNumberDescription, + final Map deviceInfoSerialNumbers) { + // check to see if the platform serial number is contained in the map of device info's + // serial numbers + for (Map.Entry entry : deviceInfoSerialNumbers.entrySet()) { + if (entry.getValue().equalsIgnoreCase(platformSerialNumber)) { + log.info("Device info contained platform {} {}" + + " in the device info's {}", platformSerialNumberDescription, + platformSerialNumber, entry.getKey()); + return true; + } + } + + log.warn("Platform {}, {}, did not match any device info serial numbers", + platformSerialNumberDescription, platformSerialNumber); + return false; + } + + /** + * Validates the information supplied for the Platform Credential. This + * method checks if the field is required and therefore if the value is + * present then verifies that the values match. + * @param platformCredentialFieldName name of field to be compared + * @param platformCredentialFieldValue first value to compare + * @param otherValue second value to compare + * @return true if values match + */ + private static boolean requiredPlatformCredentialFieldIsNonEmptyAndMatches( + final String platformCredentialFieldName, + final String platformCredentialFieldValue, + final String otherValue) { + if (hasEmptyValueForRequiredField(platformCredentialFieldName, + platformCredentialFieldValue)) { + return false; + } + + return platformCredentialFieldMatches(platformCredentialFieldName, + platformCredentialFieldValue, otherValue); + } + + /** + * Validates the information supplied for the Platform Credential. This + * method checks if the value is present then verifies that the values match. + * If not present, then returns true. + * @param platformCredentialFieldName name of field to be compared + * @param platformCredentialFieldValue first value to compare + * @param otherValue second value to compare + * @return true if values match or null + */ + private static boolean optionalPlatformCredentialFieldNullOrMatches( + final String platformCredentialFieldName, + final String platformCredentialFieldValue, + final String otherValue) { + if (platformCredentialFieldValue == null) { + return true; + } + + return platformCredentialFieldMatches(platformCredentialFieldName, + platformCredentialFieldValue, otherValue); + } + + /** + * Returns true if fieldValue is null or empty. + * @param description description of the value + * @param fieldValue value of the field + * @return true if fieldValue is null or empty; false otherwise + */ + private static boolean hasEmptyValueForRequiredField(final String description, + final String fieldValue) { + if (StringUtils.isEmpty(fieldValue)) { + log.error("Required field was empty or null in Platform Credential: " + + description); + return true; + } + return false; + } + + private static boolean platformCredentialFieldMatches( + final String platformCredentialFieldName, + final String platformCredentialFieldValue, + final String otherValue) { + String trimmedFieldValue = platformCredentialFieldValue.trim(); + String trimmedOtherValue = otherValue.trim(); + + if (!trimmedFieldValue.equals(trimmedOtherValue)) { + log.debug(String.format("%s field in Platform Credential (%s) does not match " + + "a related field in the DeviceInfoReport (%s)", + platformCredentialFieldName, trimmedFieldValue, trimmedOtherValue)); + return false; + } + + log.debug(String.format("%s field in Platform Credential matches " + + "a related field in the DeviceInfoReport (%s)", + platformCredentialFieldName, trimmedFieldValue) + ); + + return true; + } + + /** + * Returns true if fieldValue is null or empty. + * @param description description of the value + * @param fieldValue value of the field + * @return true if fieldValue is null or empty; false otherwise + */ + private static boolean hasEmptyValueForRequiredField(final String description, + final DERUTF8String fieldValue) { + if (fieldValue == null || StringUtils.isEmpty(fieldValue.getString().trim())) { + log.error("Required field was empty or null in Platform Credential: " + + description); + return true; + } + return false; + } +} diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/validation/FirmwareScvValidator.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/validation/FirmwareScvValidator.java new file mode 100644 index 00000000..d34b8233 --- /dev/null +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/validation/FirmwareScvValidator.java @@ -0,0 +1,258 @@ +package hirs.attestationca.persist.validation; + +import hirs.attestationca.persist.entity.manager.CACredentialRepository; +import hirs.attestationca.persist.entity.manager.CertificateRepository; +import hirs.attestationca.persist.entity.manager.ReferenceDigestValueRepository; +import hirs.attestationca.persist.entity.manager.ReferenceManifestRepository; +import hirs.attestationca.persist.entity.userdefined.Device; +import hirs.attestationca.persist.entity.userdefined.PolicySettings; +import hirs.attestationca.persist.entity.userdefined.ReferenceManifest; +import hirs.attestationca.persist.entity.userdefined.SupplyChainValidation; +import hirs.attestationca.persist.entity.userdefined.certificate.CertificateAuthorityCredential; +import hirs.attestationca.persist.entity.userdefined.rim.BaseReferenceManifest; +import hirs.attestationca.persist.entity.userdefined.rim.EventLogMeasurements; +import hirs.attestationca.persist.entity.userdefined.rim.ReferenceDigestValue; +import hirs.attestationca.persist.enums.AppraisalStatus; +import hirs.attestationca.persist.service.ValidationManager; +import hirs.utils.SwidResource; +import hirs.utils.tpm.eventlog.TCGEventLog; +import hirs.utils.tpm.eventlog.TpmPcrEvent; +import lombok.extern.log4j.Log4j2; +import org.apache.logging.log4j.Level; + +import java.io.IOException; +import java.security.KeyStore; +import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateException; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; + +import static hirs.attestationca.persist.enums.AppraisalStatus.Status.FAIL; +import static hirs.attestationca.persist.enums.AppraisalStatus.Status.PASS; + +@Log4j2 +public class FirmwareScvValidator extends SupplyChainCredentialValidator { + + private static PcrValidator pcrValidator; + + @SuppressWarnings("methodlength") + public static AppraisalStatus validateFirmware( + final Device device, final PolicySettings policySettings, + final ReferenceManifestRepository referenceManifestRepository, + final ReferenceDigestValueRepository referenceDigestValueRepository, + final CACredentialRepository caCredentialRepository) { + boolean passed = true; + String[] baseline = new String[Integer.SIZE]; + AppraisalStatus fwStatus = null; + String manufacturer = device.getDeviceInfo() + .getHardwareInfo().getManufacturer(); + String model = device.getDeviceInfo() + .getHardwareInfo().getProductName(); + ReferenceManifest validationObject; + List baseReferenceManifests = null; + BaseReferenceManifest baseReferenceManifest = null; + ReferenceManifest supportReferenceManifest = null; + EventLogMeasurements measurement = null; + + baseReferenceManifests = referenceManifestRepository.findAllBaseRims(); + + for (BaseReferenceManifest bRim : baseReferenceManifests) { + if (bRim.getPlatformManufacturer().equals(manufacturer) + && !bRim.isSwidSupplemental() && !bRim.isSwidPatch()) { + baseReferenceManifest = bRim; + } + } + + String failedString = ""; + if (baseReferenceManifest == null) { + failedString = "Base Reference Integrity Manifest\n"; + passed = false; + } else { + measurement = (EventLogMeasurements) referenceManifestRepository.findByHexDecHash( + baseReferenceManifest.getEventLogHash()); + + if (measurement == null) { + measurement = referenceManifestRepository.getLogByModel( + baseReferenceManifest.getPlatformModel()); + } + } + + if (measurement == null) { + failedString += "Bios measurement"; + passed = false; + } + validationObject = measurement; + + if (passed) { + List resources = + ((BaseReferenceManifest) baseReferenceManifest).getFileResources(); + fwStatus = new AppraisalStatus(PASS, + SupplyChainCredentialValidator.FIRMWARE_VALID); + + // verify signatures + ReferenceManifestValidator referenceManifestValidator = + new ReferenceManifestValidator(); + referenceManifestValidator.setRim(baseReferenceManifest); + + //Validate signing cert + List allCerts = caCredentialRepository.findAll(); + CertificateAuthorityCredential signingCert = null; + for (CertificateAuthorityCredential cert : allCerts) { + signingCert = cert; + KeyStore keyStore = ValidationManager.getCaChain(signingCert, + caCredentialRepository); + if (referenceManifestValidator.validateXmlSignature(signingCert)) { + try { + if (!SupplyChainCredentialValidator.verifyCertificate( + signingCert.getX509Certificate(), keyStore)) { + passed = false; + fwStatus = new AppraisalStatus(FAIL, + "Firmware validation failed: invalid certificate path."); + validationObject = baseReferenceManifest; + } + } catch (IOException e) { + log.error("Error getting X509 cert from manager: " + e.getMessage()); + } catch (SupplyChainValidatorException e) { + log.error("Error validating cert against keystore: " + e.getMessage()); + fwStatus = new AppraisalStatus(FAIL, + "Firmware validation failed: invalid certificate path."); + } + break; + } + } + + for (SwidResource swidRes : resources) { + supportReferenceManifest = referenceManifestRepository.findByHexDecHash( + swidRes.getHashValue()); + if (supportReferenceManifest != null) { + // Removed the filename check from this if statement + referenceManifestValidator.validateSupportRimHash( + supportReferenceManifest.getRimBytes(), swidRes.getHashValue()); + } + } + + if (passed && signingCert == null) { + passed = false; + fwStatus = new AppraisalStatus(FAIL, + "Firmware validation failed: signing cert not found."); + } + + if (passed && supportReferenceManifest == null) { + fwStatus = new AppraisalStatus(FAIL, + "Support Reference Integrity Manifest can not be found"); + passed = false; + } + + if (passed && !referenceManifestValidator.isSignatureValid()) { + passed = false; + fwStatus = new AppraisalStatus(FAIL, + "Firmware validation failed: Signature validation " + + "failed for Base RIM."); + } + + if (passed && !referenceManifestValidator.isSupportRimValid()) { + passed = false; + fwStatus = new AppraisalStatus(FAIL, + "Firmware validation failed: Hash validation " + + "failed for Support RIM."); + } + + if (passed) { + TCGEventLog logProcessor; + try { + logProcessor = new TCGEventLog(supportReferenceManifest.getRimBytes()); + baseline = logProcessor.getExpectedPCRValues(); + } catch (CertificateException cEx) { + log.error(cEx); + } catch (NoSuchAlgorithmException noSaEx) { + log.error(noSaEx); + } catch (IOException ioEx) { + log.error(ioEx); + } + + // part 1 of firmware validation check: PCR baseline match + pcrValidator = new PcrValidator(baseline); + + if (baseline.length > 0) { + String pcrContent = ""; + pcrContent = new String(device.getDeviceInfo().getTpmInfo().getPcrValues()); + + if (pcrContent.isEmpty()) { + fwStatus = new AppraisalStatus(FAIL, + "Firmware validation failed: Client did not " + + "provide pcr values."); + log.warn(String.format( + "Firmware validation failed: Client (%s) did not " + + "provide pcr values.", device.getName())); + } else { + // we have a full set of PCR values + //int algorithmLength = baseline[0].length(); + //String[] storedPcrs = buildStoredPcrs(pcrContent, algorithmLength); + //pcrPolicy.validatePcrs(storedPcrs); + + // part 2 of firmware validation check: bios measurements + // vs baseline tcg event log + // find the measurement + TCGEventLog tcgMeasurementLog; + LinkedList tpmPcrEvents = new LinkedList<>(); + List eventValue; + HashMap eventValueMap = new HashMap<>(); + try { + if (measurement.getPlatformManufacturer().equals(manufacturer)) { + tcgMeasurementLog = new TCGEventLog(measurement.getRimBytes()); + eventValue = referenceDigestValueRepository + .findValuesByBaseRimId(baseReferenceManifest.getId()); + for (ReferenceDigestValue rdv : eventValue) { + eventValueMap.put(rdv.getDigestValue(), rdv); + } + + tpmPcrEvents.addAll(pcrValidator.validateTpmEvents( + tcgMeasurementLog, eventValueMap, policySettings)); + } + } catch (CertificateException cEx) { + log.error(cEx); + } catch (NoSuchAlgorithmException noSaEx) { + log.error(noSaEx); + } catch (IOException ioEx) { + log.error(ioEx); + } + + if (!tpmPcrEvents.isEmpty()) { + StringBuilder sb = new StringBuilder(); + validationObject = measurement; + sb.append(String.format("%d digest(s) were not found:%n", + tpmPcrEvents.size())); + for (TpmPcrEvent tpe : tpmPcrEvents) { + sb.append(String.format("PCR Index %d - %s%n", + tpe.getPcrIndex(), + tpe.getEventTypeStr())); + } + if (fwStatus.getAppStatus().equals(FAIL)) { + fwStatus = new AppraisalStatus(FAIL, String.format("%s%n%s", + fwStatus.getMessage(), sb.toString())); + } else { + fwStatus = new AppraisalStatus(FAIL, sb.toString()); + } + } + } + } else { + fwStatus = new AppraisalStatus(FAIL, "The RIM baseline could not be found."); + } + } + + EventLogMeasurements eventLog = measurement; + eventLog.setOverallValidationResult(fwStatus.getAppStatus()); + referenceManifestRepository.save(eventLog); + } else { + fwStatus = new AppraisalStatus(FAIL, String.format("Firmware Validation failed: " + + "%s for %s can not be found", failedString, manufacturer)); + if (measurement != null) { + measurement.setOverallValidationResult(fwStatus.getAppStatus()); + referenceManifestRepository.save(measurement); + } + } + + return fwStatus; + } +} From cb5b281d03b24c75f72f4710117151aa4229c498 Mon Sep 17 00:00:00 2001 From: Cyrus <24922493+cyrus-dev@users.noreply.github.com> Date: Thu, 21 Sep 2023 16:25:01 -0400 Subject: [PATCH 03/15] Missed updated constructor for Device --- .../persist/entity/userdefined/Device.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/Device.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/Device.java index b01c0bbb..118cd3d7 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/Device.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/Device.java @@ -58,6 +58,16 @@ public class Device extends AbstractEntity { @Column(name = "summary_id") private String summaryId; + public Device(final DeviceInfoReport deviceInfoReport) { + super(); + if (deviceInfoReport != null) { + this.name = deviceInfoReport.getNetworkInfo().getHostname(); + this.deviceInfo = deviceInfoReport; + } else { + name = ""; + } + } + public String toString() { return String.format("Device Name: %s%nStatus: %s%nSummary: %s", name, healthStatus.getStatus(), From a08c007bba104c2ffebfca997f72e46944d98ab6 Mon Sep 17 00:00:00 2001 From: Cyrus <24922493+cyrus-dev@users.noreply.github.com> Date: Fri, 22 Sep 2023 11:50:28 -0400 Subject: [PATCH 04/15] Some additional updates and changes while debugging --- .../persist/entity/AbstractEntity.java | 2 +- .../attributes/ComponentClass.java | 2 +- .../provision/AbstractRequestHandler.java | 5 ++-- .../service/SupplyChainValidationService.java | 9 +++--- .../validation/CredentialValidator.java | 13 +++++++++ .../SupplyChainCredentialValidator.java | 4 --- .../ValidationReportsPageController.java | 29 ------------------- .../utils/tpm/eventlog/uefi/UefiGuid.java | 4 +-- 8 files changed, 24 insertions(+), 44 deletions(-) diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/AbstractEntity.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/AbstractEntity.java index a647b1da..204b6dfd 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/AbstractEntity.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/AbstractEntity.java @@ -40,7 +40,7 @@ public abstract class AbstractEntity implements Serializable { @Column (name = "create_time") @ColumnDefault(value = "CURRENT_TIMESTAMP") @Generated(GenerationTime.INSERT) - private Date createTime; + private Date createTime = new Date(); /** * Default empty constructor is required for Hibernate. It is protected to diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/attributes/ComponentClass.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/attributes/ComponentClass.java index a666f4e3..40d9a5cc 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/attributes/ComponentClass.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/attributes/ComponentClass.java @@ -30,7 +30,7 @@ public class ComponentClass { private static final String TCG_COMPONENT_REGISTRY = "2.23.133.18.3.1"; private static final String SMBIOS_COMPONENT_REGISTRY = "2.23.133.18.3.3"; private static final Path JSON_PATH = FileSystems.getDefault() - .getPath("/etc", "hirs/aca", "default-properties", "component-class.json"); + .getPath("/opt", "hirs", "default-properties", "component-class.json"); private static final String OTHER_STRING = "Other"; private static final String UNKNOWN_STRING = "Unknown"; diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/AbstractRequestHandler.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/AbstractRequestHandler.java index 18bb3f54..14ed80ba 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/AbstractRequestHandler.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/AbstractRequestHandler.java @@ -233,8 +233,8 @@ public class AbstractRequestHandler { final Device device) { IssuedAttestationCertificate issuedAc; boolean generateCertificate = true; - PolicyRepository scp = this.getPolicyRepository(); - PolicySettings policySettings = scp.findByName("Default"); + PolicyRepository scp = getPolicyRepository(); + PolicySettings policySettings; Date currentDate = new Date(); int days; try { @@ -243,6 +243,7 @@ public class AbstractRequestHandler { derEncodedAttestationCertificate, endorsementCredential, platformCredentials); if (scp != null) { + policySettings = scp.findByName("Default"); issuedAc = certificateRepository.findByDeviceId(device.getId()); generateCertificate = policySettings.isIssueAttestationCertificate(); diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/service/SupplyChainValidationService.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/service/SupplyChainValidationService.java index 1c6e9ec1..1ef73d12 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/service/SupplyChainValidationService.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/service/SupplyChainValidationService.java @@ -105,9 +105,9 @@ public class SupplyChainValidationService { .ValidationType.PLATFORM_CREDENTIAL; log.info("Beginning Supply Chain Validation..."); - log.info("Beginning Endorsement Credential Validation..."); // Validate the Endorsement Credential if (getPolicySettings().isEcValidationEnabled()) { + log.info("Beginning Endorsement Credential Validation..."); validations.add(ValidationManager.evaluateEndorsementCredentialStatus(ec, this.caCredentialRepository, acceptExpiredCerts)); // store the device with the credential if (ec != null) { @@ -116,9 +116,9 @@ public class SupplyChainValidationService { } } - log.info("Beginning Platform Credential Validation..."); // Validate Platform Credential signatures if (getPolicySettings().isPcValidationEnabled()) { + log.info("Beginning Platform Credential Validation..."); // Ensure there are platform credentials to validate if (pcs == null || pcs.isEmpty()) { log.error("There were no Platform Credentials to validate."); @@ -143,7 +143,6 @@ public class SupplyChainValidationService { } pc.setDeviceId(device.getId()); this.certificateRepository.save(pc); - } // check that the delta certificates validity date is after @@ -179,10 +178,10 @@ public class SupplyChainValidationService { } } - log.info("Beginning Platform Attributes Validation..."); // Validate Platform Credential attributes if (getPolicySettings().isPcAttributeValidationEnabled() && pcErrorMessage.isEmpty()) { + log.info("Beginning Platform Attributes Validation..."); // Ensure there are platform credentials to validate SupplyChainValidation attributeScv = null; String attrErrorMessage = ""; @@ -233,8 +232,8 @@ public class SupplyChainValidationService { } } - log.info("Beginning Firmware Validation..."); if (getPolicySettings().isFirmwareValidationEnabled()) { + log.info("Beginning Firmware Validation..."); // may need to associated with device to pull the correct info // compare tpm quote with what is pulled from RIM associated file validations.add(ValidationManager.evaluateFirmwareStatus(device, getPolicySettings(), diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/validation/CredentialValidator.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/validation/CredentialValidator.java index 2084cfe9..52e75efe 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/validation/CredentialValidator.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/validation/CredentialValidator.java @@ -47,6 +47,19 @@ public class CredentialValidator extends SupplyChainCredentialValidator { return new AppraisalStatus(FAIL, message); } + boolean keyInStore = false; + try { + keyInStore = trustStore.size() < 1; + } catch (KeyStoreException ksEx) { + log.error(ksEx.getMessage()); + } + + if (keyInStore) { + message = baseErrorMessage + "keys in the trust store"; + log.error(message); + return new AppraisalStatus(FAIL, message); + } + try { X509Certificate verifiableCert = ec.getX509Certificate(); diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/validation/SupplyChainCredentialValidator.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/validation/SupplyChainCredentialValidator.java index bcdc9d92..d951fe54 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/validation/SupplyChainCredentialValidator.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/validation/SupplyChainCredentialValidator.java @@ -3,15 +3,11 @@ package hirs.attestationca.persist.validation; import com.fasterxml.jackson.core.JsonFactory; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; -import hirs.attestationca.persist.entity.userdefined.certificate.ComponentResult; -import hirs.attestationca.persist.entity.userdefined.certificate.attributes.ComponentIdentifier; -import hirs.attestationca.persist.entity.userdefined.certificate.attributes.V2.ComponentIdentifierV2; import hirs.attestationca.persist.entity.userdefined.info.ComponentInfo; import lombok.NoArgsConstructor; import lombok.extern.log4j.Log4j2; import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.util.Strings; -import org.bouncycastle.asn1.DERUTF8String; import org.bouncycastle.asn1.x500.X500Name; import org.bouncycastle.cert.CertException; import org.bouncycastle.cert.X509AttributeCertificateHolder; diff --git a/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/ValidationReportsPageController.java b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/ValidationReportsPageController.java index 590a5030..e2a5f5db 100644 --- a/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/ValidationReportsPageController.java +++ b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/ValidationReportsPageController.java @@ -2,13 +2,11 @@ package hirs.attestationca.portal.page.controllers; import com.google.gson.JsonArray; import com.google.gson.JsonObject; -import hirs.attestationca.persist.CriteriaModifier; import hirs.attestationca.persist.FilteredRecordsList; import hirs.attestationca.persist.entity.manager.CertificateRepository; import hirs.attestationca.persist.entity.manager.DeviceRepository; import hirs.attestationca.persist.entity.manager.PlatformCertificateRepository; import hirs.attestationca.persist.entity.manager.SupplyChainValidationSummaryRepository; -import hirs.attestationca.persist.entity.userdefined.Certificate; import hirs.attestationca.persist.entity.userdefined.Device; import hirs.attestationca.persist.entity.userdefined.SupplyChainValidationSummary; import hirs.attestationca.persist.entity.userdefined.certificate.PlatformCredential; @@ -16,18 +14,13 @@ import hirs.attestationca.persist.entity.userdefined.certificate.attributes.Comp import hirs.attestationca.persist.entity.userdefined.certificate.attributes.V2.ComponentIdentifierV2; import hirs.attestationca.portal.datatables.DataTableInput; import hirs.attestationca.portal.datatables.DataTableResponse; -import hirs.attestationca.portal.datatables.OrderedListQueryDataTableAdapter; import hirs.attestationca.portal.page.Page; import hirs.attestationca.portal.page.PageController; import hirs.attestationca.portal.page.params.NoPageParams; import jakarta.persistence.EntityManager; -import jakarta.persistence.criteria.CriteriaBuilder; -import jakarta.persistence.criteria.CriteriaQuery; -import jakarta.persistence.criteria.Root; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import lombok.extern.log4j.Log4j2; -import org.hibernate.Session; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; @@ -43,7 +36,6 @@ import org.springframework.web.servlet.ModelAndView; import java.io.BufferedWriter; import java.io.IOException; import java.io.OutputStreamWriter; -import java.lang.ref.Reference; import java.nio.charset.StandardCharsets; import java.time.LocalDate; import java.time.LocalDateTime; @@ -52,7 +44,6 @@ import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.Enumeration; import java.util.List; -import java.util.UUID; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -128,20 +119,6 @@ public class ValidationReportsPageController extends PageController scvRoot = criteriaQuery.from(Reference.class); - - criteriaQuery.select(scvRoot).distinct(true).where(cb.isNull(scvRoot.get(Certificate.ARCHIVE_FIELD))); - } - }; - FilteredRecordsList records = new FilteredRecordsList<>(); int currentPage = input.getStart() / input.getLength(); Pageable paging = PageRequest.of(currentPage, input.getLength(), Sort.by(orderColumnName)); @@ -153,12 +130,6 @@ public class ValidationReportsPageController extends PageController records = -// OrderedListQueryDataTableAdapter.getOrderedList( -// SupplyChainValidationSummary.class, -// supplyChainValidatorSummaryRepository, input, orderColumnName, -// criteriaModifier); - return new DataTableResponse<>(records, input); } diff --git a/HIRS_Utils/src/main/java/hirs/utils/tpm/eventlog/uefi/UefiGuid.java b/HIRS_Utils/src/main/java/hirs/utils/tpm/eventlog/uefi/UefiGuid.java index aa83f937..5530258c 100644 --- a/HIRS_Utils/src/main/java/hirs/utils/tpm/eventlog/uefi/UefiGuid.java +++ b/HIRS_Utils/src/main/java/hirs/utils/tpm/eventlog/uefi/UefiGuid.java @@ -23,8 +23,8 @@ public class UefiGuid { */ private static final int UUID_EPOCH_DIVISOR = 10000; - private static final Path JSON_PATH = FileSystems.getDefault().getPath("/etc", - "hirs/aca", "default-properties", "vendor-table.json"); + private static final Path JSON_PATH = FileSystems.getDefault().getPath("/opt", + "hirs", "default-properties", "vendor-table.json"); private JsonObject uefiVendorRef; /** * guid byte array. From 9c0d934331ea1de9ff29477de3926b963a968638 Mon Sep 17 00:00:00 2001 From: Cyrus <24922493+cyrus-dev@users.noreply.github.com> Date: Mon, 25 Sep 2023 10:38:33 -0400 Subject: [PATCH 05/15] Fixed icon display issue. The validationResult name changed --- .../src/main/webapp/WEB-INF/jsp/validation-reports.jsp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) 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 a42b1336..f74fdbe8 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 @@ -68,17 +68,17 @@ // create status icon var result = full.overallValidationResult; - var ovallMessage = full.message; + var overallMessage = full.message; if (result) { switch (result) { case "PASS": html += ''; break; case "FAIL": - html += ''; + html += ''; break; case "ERROR": - html += ''; + html += ''; break; default: html += unknownStatus; @@ -183,7 +183,7 @@ // the validation_type. for (var i = 0; i < full.validations.length; i++) { var curValidation = full.validations[i]; - var curResult = curValidation.result; + var curResult = curValidation.validationResult; var curMessage = curValidation.message; if (curValidation.validationType === validation_type) { From c046851e07a98d91a977c39f3017aa5837d4d631 Mon Sep 17 00:00:00 2001 From: Cyrus <24922493+cyrus-dev@users.noreply.github.com> Date: Thu, 28 Sep 2023 06:47:43 -0400 Subject: [PATCH 06/15] Latest changes that fixes little issues --- .../persist/entity/AbstractEntity.java | 3 ++ .../service/SupplyChainValidationService.java | 5 +++ ...eferenceManifestDetailsPageController.java | 8 ++--- .../ReferenceManifestPageController.java | 31 +++---------------- .../src/main/webapp/WEB-INF/jsp/error.jsp | 18 +++++++++-- .../src/main/webapp/WEB-INF/jsp/help.jsp | 6 +++- 6 files changed, 37 insertions(+), 34 deletions(-) diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/AbstractEntity.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/AbstractEntity.java index 204b6dfd..e89249c6 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/AbstractEntity.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/AbstractEntity.java @@ -67,6 +67,9 @@ public abstract class AbstractEntity implements Serializable { * @return creation time */ public Date getCreateTime() { + if (createTime == null) { + createTime = new Date(); + } return (Date) createTime.clone(); } diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/service/SupplyChainValidationService.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/service/SupplyChainValidationService.java index 1ef73d12..c3ac6a91 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/service/SupplyChainValidationService.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/service/SupplyChainValidationService.java @@ -8,6 +8,7 @@ import hirs.attestationca.persist.entity.manager.ComponentResultRepository; import hirs.attestationca.persist.entity.manager.PolicyRepository; import hirs.attestationca.persist.entity.manager.ReferenceDigestValueRepository; import hirs.attestationca.persist.entity.manager.ReferenceManifestRepository; +import hirs.attestationca.persist.entity.manager.SupplyChainValidationRepository; import hirs.attestationca.persist.entity.manager.SupplyChainValidationSummaryRepository; import hirs.attestationca.persist.entity.userdefined.Device; import hirs.attestationca.persist.entity.userdefined.PolicySettings; @@ -47,6 +48,7 @@ public class SupplyChainValidationService { private ReferenceDigestValueRepository referenceDigestValueRepository; private ComponentResultRepository componentResultRepository; private CertificateRepository certificateRepository; + private SupplyChainValidationRepository supplyChainValidationRepository; private SupplyChainValidationSummaryRepository supplyChainValidationSummaryRepository; /** @@ -57,6 +59,7 @@ public class SupplyChainValidationService { * @param certificateRepository the cert manager * @param componentResultRepository the comp result manager * @param referenceManifestRepository the RIM manager + * @param supplyChainValidationRepository the scv manager * @param supplyChainValidationSummaryRepository the summary manager * @param referenceDigestValueRepository the even manager */ @@ -68,6 +71,7 @@ public class SupplyChainValidationService { final CertificateRepository certificateRepository, final ComponentResultRepository componentResultRepository, final ReferenceManifestRepository referenceManifestRepository, + final SupplyChainValidationRepository supplyChainValidationRepository, final SupplyChainValidationSummaryRepository supplyChainValidationSummaryRepository, final ReferenceDigestValueRepository referenceDigestValueRepository) { this.caCredentialRepository = caCredentialRepository; @@ -75,6 +79,7 @@ public class SupplyChainValidationService { this.certificateRepository = certificateRepository; this.componentResultRepository = componentResultRepository; this.referenceManifestRepository = referenceManifestRepository; + this.supplyChainValidationRepository = supplyChainValidationRepository; this.supplyChainValidationSummaryRepository = supplyChainValidationSummaryRepository; this.referenceDigestValueRepository = referenceDigestValueRepository; } diff --git a/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/ReferenceManifestDetailsPageController.java b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/ReferenceManifestDetailsPageController.java index 110bbd00..cd07a8a1 100644 --- a/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/ReferenceManifestDetailsPageController.java +++ b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/ReferenceManifestDetailsPageController.java @@ -266,8 +266,8 @@ public class ReferenceManifestDetailsPageController extends PageController rimRoot = criteriaQuery.from(Reference.class); - - criteriaQuery.select(rimRoot).distinct(true).where(cb.isNull(rimRoot.get(Certificate.ARCHIVE_FIELD))); - } - }; - log.info("Querying with the following dataTableInput: " + input.toString()); FilteredRecordsList records = new FilteredRecordsList<>(); @@ -144,15 +124,14 @@ public class ReferenceManifestPageController extends PageController pagedResult = referenceManifestRepository.findAll(paging); if (pagedResult.hasContent()) { - records.addAll(pagedResult.getContent()); + for (ReferenceManifest manifest : pagedResult.getContent()) { + if (!manifest.getRimType().equals(ReferenceManifest.MEASUREMENT_RIM)) { + records.add(manifest); + } + } } records.setRecordsTotal(input.getLength()); records.setRecordsFiltered(referenceManifestRepository.count()); -// FilteredRecordsList records -// = OrderedListQueryDataTableAdapter.getOrderedList( -// ReferenceManifest.class, -// this.referenceManifestRepository, -// input, orderColumnName, criteriaModifier); log.debug("Returning list of size: " + records.size()); return new DataTableResponse<>(records, input); diff --git a/HIRS_AttestationCAPortal/src/main/webapp/WEB-INF/jsp/error.jsp b/HIRS_AttestationCAPortal/src/main/webapp/WEB-INF/jsp/error.jsp index da7f58f9..88bb8877 100644 --- a/HIRS_AttestationCAPortal/src/main/webapp/WEB-INF/jsp/error.jsp +++ b/HIRS_AttestationCAPortal/src/main/webapp/WEB-INF/jsp/error.jsp @@ -1,3 +1,15 @@ -

-Page Not Found! Devices -
\ No newline at end of file +<%@page contentType="text/html" pageEncoding="UTF-8"%> + +<%-- JSP TAGS --%> +<%@taglib prefix="c" uri="jakarta.tags.core" %> +<%@taglib prefix="my" tagdir="/WEB-INF/tags"%> + +<%-- CONTENT --%> + + Error - 404 + + + + + \ No newline at end of file diff --git a/HIRS_AttestationCAPortal/src/main/webapp/WEB-INF/jsp/help.jsp b/HIRS_AttestationCAPortal/src/main/webapp/WEB-INF/jsp/help.jsp index 4ec2a763..c6d478a3 100644 --- a/HIRS_AttestationCAPortal/src/main/webapp/WEB-INF/jsp/help.jsp +++ b/HIRS_AttestationCAPortal/src/main/webapp/WEB-INF/jsp/help.jsp @@ -10,12 +10,16 @@

Documentation

+ -

For more documentation on the project, you may visit the wiki section of our code repository.

+

+ For more documentation on the project, you may visit the wiki section of our code repository. +

\ No newline at end of file From 8be945035dde8118956653074a9efd219de7873d Mon Sep 17 00:00:00 2001 From: Cyrus <24922493+cyrus-dev@users.noreply.github.com> Date: Thu, 28 Sep 2023 17:11:34 -0400 Subject: [PATCH 07/15] Some additional updates to fix visual changes on the screen. This one is mainly with deviceName --- .../provision/AbstractRequestHandler.java | 4 +++- .../provision/IdentityClaimHandler.java | 2 +- .../provision/IdentityRequestHandler.java | 2 +- .../helper/CredentialManagementHelper.java | 4 +++- .../validation/CredentialValidator.java | 4 ++-- .../validation/FirmwareScvValidator.java | 21 +++++++++---------- .../controllers/DevicePageController.java | 6 +++--- .../jsp/endorsement-key-credentials.jsp | 8 +------ .../WEB-INF/jsp/issued-certificates.jsp | 7 +------ .../WEB-INF/jsp/platform-credentials.jsp | 8 +------ 10 files changed, 26 insertions(+), 40 deletions(-) diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/AbstractRequestHandler.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/AbstractRequestHandler.java index 14ed80ba..c7c7e27c 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/AbstractRequestHandler.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/AbstractRequestHandler.java @@ -137,7 +137,8 @@ public class AbstractRequestHandler { if (identityClaim.hasEndorsementCredential()) { endorsementCredential = CredentialManagementHelper.storeEndorsementCredential( certificateRepository, - identityClaim.getEndorsementCredential().toByteArray()); + identityClaim.getEndorsementCredential().toByteArray(), + identityClaim.getDv().getNw().getHostname()); } else if (ekPub != null) { log.warn("Endorsement Cred was not in the identity claim from the client." + " Checking for uploads."); @@ -261,6 +262,7 @@ public class AbstractRequestHandler { } } if (generateCertificate) { + attCert.setDeviceId(device.getId()); attCert.setDeviceName(device.getName()); certificateRepository.save(attCert); } diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/IdentityClaimHandler.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/IdentityClaimHandler.java index 4dc3458c..07fc0f52 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/IdentityClaimHandler.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/IdentityClaimHandler.java @@ -105,7 +105,7 @@ public class IdentityClaimHandler extends AbstractRequestHandler { * @return an identity claim response for the specified request containing a wrapped blob */ public byte[] processIdentityClaimTpm2(final byte[] identityClaim) { - log.error("Identity Claim received..."); + log.info("Identity Claim received..."); if (ArrayUtils.isEmpty(identityClaim)) { log.error("Identity claim empty throwing exception."); diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/IdentityRequestHandler.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/IdentityRequestHandler.java index 142179e4..b2188d56 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/IdentityRequestHandler.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/IdentityRequestHandler.java @@ -110,7 +110,7 @@ public class IdentityRequestHandler extends AbstractRequestHandler { byte[] ecBytesFromIdentityRequest = proof.getEndorsementCredential(); if (ArrayUtils.isNotEmpty(ecBytesFromIdentityRequest)) { endorsementCredential = CredentialManagementHelper.storeEndorsementCredential( - this.certificateRepository, ecBytesFromIdentityRequest); + this.certificateRepository, ecBytesFromIdentityRequest, ""); try { BigInteger publicKeyModulus = Certificate.getPublicKeyModulus( endorsementCredential.getX509Certificate()); diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/helper/CredentialManagementHelper.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/helper/CredentialManagementHelper.java index 1b99e2e2..4d684665 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/helper/CredentialManagementHelper.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/helper/CredentialManagementHelper.java @@ -27,12 +27,13 @@ public final class CredentialManagementHelper { * it is unarchived. * @param certificateRepository the certificate manager used for storage * @param endorsementBytes the raw EK bytes used for parsing + * @param deviceName the host name * @return the parsed, valid EK * @throws IllegalArgumentException if the provided bytes are not a valid EK. */ public static EndorsementCredential storeEndorsementCredential( final CertificateRepository certificateRepository, - final byte[] endorsementBytes) throws IllegalArgumentException { + final byte[] endorsementBytes, final String deviceName) throws IllegalArgumentException { if (certificateRepository == null) { throw new IllegalArgumentException("null certificate manager"); @@ -64,6 +65,7 @@ public final class CredentialManagementHelper { .findByCertificateHash(certificateHash); if (existingCredential == null) { log.info("No Endorsement Credential found with hash: " + certificateHash); + endorsementCredential.setDeviceName(deviceName); return (EndorsementCredential) certificateRepository.save(endorsementCredential); } else if (existingCredential.isArchived()) { // if the EK is stored in the DB and it's archived, unarchive. diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/validation/CredentialValidator.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/validation/CredentialValidator.java index 52e75efe..be48a5e7 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/validation/CredentialValidator.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/validation/CredentialValidator.java @@ -109,13 +109,13 @@ public class CredentialValidator extends SupplyChainCredentialValidator { String message; String certVerifyMsg; if (pc == null) { - message = baseErrorMessage + "a platform credential\n"; + message = baseErrorMessage + "a platform credential"; log.error(message); return new AppraisalStatus(FAIL, message); } try { if (trustStore == null || trustStore.size() == 0) { - message = baseErrorMessage + "an Issuer Cert in the Trust Store\n"; + message = baseErrorMessage + "an Issuer Cert in the Trust Store"; log.error(message); return new AppraisalStatus(FAIL, message); } diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/validation/FirmwareScvValidator.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/validation/FirmwareScvValidator.java index d34b8233..fb1778c0 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/validation/FirmwareScvValidator.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/validation/FirmwareScvValidator.java @@ -45,10 +45,9 @@ public class FirmwareScvValidator extends SupplyChainCredentialValidator { boolean passed = true; String[] baseline = new String[Integer.SIZE]; AppraisalStatus fwStatus = null; + String hostName = device.getDeviceInfo().getNetworkInfo().getHostname(); String manufacturer = device.getDeviceInfo() .getHardwareInfo().getManufacturer(); - String model = device.getDeviceInfo() - .getHardwareInfo().getProductName(); ReferenceManifest validationObject; List baseReferenceManifests = null; BaseReferenceManifest baseReferenceManifest = null; @@ -58,7 +57,7 @@ public class FirmwareScvValidator extends SupplyChainCredentialValidator { baseReferenceManifests = referenceManifestRepository.findAllBaseRims(); for (BaseReferenceManifest bRim : baseReferenceManifests) { - if (bRim.getPlatformManufacturer().equals(manufacturer) + if (bRim.getDeviceName().equals(hostName) && !bRim.isSwidSupplemental() && !bRim.isSwidPatch()) { baseReferenceManifest = bRim; } @@ -73,8 +72,8 @@ public class FirmwareScvValidator extends SupplyChainCredentialValidator { baseReferenceManifest.getEventLogHash()); if (measurement == null) { - measurement = referenceManifestRepository.getLogByModel( - baseReferenceManifest.getPlatformModel()); + measurement = referenceManifestRepository.byMeasurementDeviceName( + baseReferenceManifest.getDeviceName()); } } @@ -111,10 +110,10 @@ public class FirmwareScvValidator extends SupplyChainCredentialValidator { "Firmware validation failed: invalid certificate path."); validationObject = baseReferenceManifest; } - } catch (IOException e) { - log.error("Error getting X509 cert from manager: " + e.getMessage()); - } catch (SupplyChainValidatorException e) { - log.error("Error validating cert against keystore: " + e.getMessage()); + } catch (IOException ioEx) { + log.error("Error getting X509 cert from manager: " + ioEx.getMessage()); + } catch (SupplyChainValidatorException scvEx) { + log.error("Error validating cert against keystore: " + scvEx.getMessage()); fwStatus = new AppraisalStatus(FAIL, "Firmware validation failed: invalid certificate path."); } @@ -199,7 +198,7 @@ public class FirmwareScvValidator extends SupplyChainCredentialValidator { List eventValue; HashMap eventValueMap = new HashMap<>(); try { - if (measurement.getPlatformManufacturer().equals(manufacturer)) { + if (measurement.getDeviceName().equals(hostName)) { tcgMeasurementLog = new TCGEventLog(measurement.getRimBytes()); eventValue = referenceDigestValueRepository .findValuesByBaseRimId(baseReferenceManifest.getId()); @@ -246,7 +245,7 @@ public class FirmwareScvValidator extends SupplyChainCredentialValidator { referenceManifestRepository.save(eventLog); } else { fwStatus = new AppraisalStatus(FAIL, String.format("Firmware Validation failed: " - + "%s for %s can not be found", failedString, manufacturer)); + + "%s for %s can not be found", failedString, hostName)); if (measurement != null) { measurement.setOverallValidationResult(fwStatus.getAppStatus()); referenceManifestRepository.save(measurement); diff --git a/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/DevicePageController.java b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/DevicePageController.java index a7a479da..693e85a2 100644 --- a/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/DevicePageController.java +++ b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/DevicePageController.java @@ -131,10 +131,11 @@ public class DevicePageController extends PageController { issuedCertificateList.addAll(issuedCertificateRepository.findByDeviceId(id)); } + HashMap> certificatePropertyMap; // loop all the devices for (Device device : deviceList) { // hashmap containing the list of certificates based on the certificate type - HashMap> certificatePropertyMap = new HashMap<>(); + certificatePropertyMap = new HashMap<>(); deviceCertMap.put("device", device); String deviceName; @@ -179,8 +180,7 @@ public class DevicePageController extends PageController { } for (IssuedAttestationCertificate ic : issuedCertificateList) { - deviceName = deviceRepository.findById(ic.getDeviceId()).get().getName(); - + deviceName = ic.getDeviceName(); // set the certificate if it's the same ID if (device.getName().equals(deviceName)) { String certificateId = IssuedAttestationCertificate.class.getSimpleName(); diff --git a/HIRS_AttestationCAPortal/src/main/webapp/WEB-INF/jsp/endorsement-key-credentials.jsp b/HIRS_AttestationCAPortal/src/main/webapp/WEB-INF/jsp/endorsement-key-credentials.jsp index 75706028..e31d10ed 100644 --- a/HIRS_AttestationCAPortal/src/main/webapp/WEB-INF/jsp/endorsement-key-credentials.jsp +++ b/HIRS_AttestationCAPortal/src/main/webapp/WEB-INF/jsp/endorsement-key-credentials.jsp @@ -52,13 +52,7 @@ data: 'deviceName', render: function (data, type, full, meta) { // if there's a device, display its name, otherwise - // display nothing - if (full.device) { - // TODO render a link to a device details page, - // passing the device.id - return full.deviceName; - } - return ''; + return full.deviceName; } }, {data: 'issuer'}, diff --git a/HIRS_AttestationCAPortal/src/main/webapp/WEB-INF/jsp/issued-certificates.jsp b/HIRS_AttestationCAPortal/src/main/webapp/WEB-INF/jsp/issued-certificates.jsp index 0ed637bb..4e2be128 100644 --- a/HIRS_AttestationCAPortal/src/main/webapp/WEB-INF/jsp/issued-certificates.jsp +++ b/HIRS_AttestationCAPortal/src/main/webapp/WEB-INF/jsp/issued-certificates.jsp @@ -48,12 +48,7 @@ render: function (data, type, full, meta) { // if there's a device, display its name, otherwise // display nothing - if (full.device) { - // TODO render a link to a device details page, - // passing the device.id - return full.deviceName; - } - return ''; + return full.deviceName; } }, {data: 'issuer'}, diff --git a/HIRS_AttestationCAPortal/src/main/webapp/WEB-INF/jsp/platform-credentials.jsp b/HIRS_AttestationCAPortal/src/main/webapp/WEB-INF/jsp/platform-credentials.jsp index defb07e1..178c0391 100644 --- a/HIRS_AttestationCAPortal/src/main/webapp/WEB-INF/jsp/platform-credentials.jsp +++ b/HIRS_AttestationCAPortal/src/main/webapp/WEB-INF/jsp/platform-credentials.jsp @@ -57,13 +57,7 @@ data: 'deviceName', render: function (data, type, full, meta) { // if there's a device, display its name, otherwise - // display nothing - if (full.device) { - // TODO render a link to a device details page, - // passing the device.id - return full.deviceName; - } - return ''; + return full.deviceName; } }, {data: 'issuer'}, From 31066694eee24c7168fbf81d8b816d84d9cf598a Mon Sep 17 00:00:00 2001 From: Cyrus <24922493+cyrus-dev@users.noreply.github.com> Date: Fri, 29 Sep 2023 08:18:54 -0400 Subject: [PATCH 08/15] Updates to the location of default property files and setting the deviceName --- .../attributes/ComponentClass.java | 2 +- .../service/SupplyChainValidationService.java | 2 ++ .../CertificatePageController.java | 24 +++++++++---------- .../utils/tpm/eventlog/uefi/UefiGuid.java | 9 +++---- 4 files changed, 18 insertions(+), 19 deletions(-) diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/attributes/ComponentClass.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/attributes/ComponentClass.java index 40d9a5cc..21509339 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/attributes/ComponentClass.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/attributes/ComponentClass.java @@ -30,7 +30,7 @@ public class ComponentClass { private static final String TCG_COMPONENT_REGISTRY = "2.23.133.18.3.1"; private static final String SMBIOS_COMPONENT_REGISTRY = "2.23.133.18.3.3"; private static final Path JSON_PATH = FileSystems.getDefault() - .getPath("/opt", "hirs", "default-properties", "component-class.json"); + .getPath("/etc", "hirs", "aca", "default-properties", "component-class.json"); private static final String OTHER_STRING = "Other"; private static final String UNKNOWN_STRING = "Unknown"; diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/service/SupplyChainValidationService.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/service/SupplyChainValidationService.java index c3ac6a91..e9e44d77 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/service/SupplyChainValidationService.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/service/SupplyChainValidationService.java @@ -117,6 +117,7 @@ public class SupplyChainValidationService { // store the device with the credential if (ec != null) { ec.setDeviceId(device.getId()); + ec.setDeviceName(device.getDeviceInfo().getNetworkInfo().getHostname()); this.certificateRepository.save(ec); } } @@ -147,6 +148,7 @@ public class SupplyChainValidationService { deltaMapping.put(pc, null); } pc.setDeviceId(device.getId()); + pc.setDeviceName(device.getDeviceInfo().getNetworkInfo().getHostname()); this.certificateRepository.save(pc); } diff --git a/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/CertificatePageController.java b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/CertificatePageController.java index f6f6a59a..60a81689 100644 --- a/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/CertificatePageController.java +++ b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/CertificatePageController.java @@ -832,11 +832,11 @@ public class CertificatePageController extends PageController { log.error(failMessage, dEx); messages.addError(failMessage + dEx.getMessage()); return null; - } catch (IllegalArgumentException e) { + } catch (IllegalArgumentException iaEx) { final String failMessage = String.format( "Certificate format not recognized(%s): ", fileName); - log.error(failMessage, e); - messages.addError(failMessage + e.getMessage()); + log.error(failMessage, iaEx); + messages.addError(failMessage + iaEx.getMessage()); return null; } } @@ -864,11 +864,11 @@ public class CertificatePageController extends PageController { existingCertificate = getCertificateByHash( certificateType, certificate.getCertificateHash()); - } catch (DBServiceException e) { + } catch (DBServiceException dbsEx) { final String failMessage = "Querying for existing certificate failed (" + fileName + "): "; - messages.addError(failMessage + e.getMessage()); - log.error(failMessage, e); + messages.addError(failMessage + dbsEx.getMessage()); + log.error(failMessage, dbsEx); return; } @@ -924,11 +924,11 @@ public class CertificatePageController extends PageController { log.info(successMsg); return; } - } catch (DBServiceException e) { + } catch (DBServiceException dbsEx) { final String failMessage = String.format("Storing new certificate failed (%s): ", fileName); - messages.addError(failMessage + e.getMessage()); - log.error(failMessage, e); + messages.addError(failMessage + dbsEx.getMessage()); + log.error(failMessage, dbsEx); return; } @@ -946,12 +946,12 @@ public class CertificatePageController extends PageController { log.info(successMsg); return; } - } catch (DBServiceException e) { + } catch (DBServiceException dbsEx) { final String failMessage = String.format("Found an identical" + " pre-existing certificate in the " + "archive, but failed to unarchive it (%s): ", fileName); - messages.addError(failMessage + e.getMessage()); - log.error(failMessage, e); + messages.addError(failMessage + dbsEx.getMessage()); + log.error(failMessage, dbsEx); return; } diff --git a/HIRS_Utils/src/main/java/hirs/utils/tpm/eventlog/uefi/UefiGuid.java b/HIRS_Utils/src/main/java/hirs/utils/tpm/eventlog/uefi/UefiGuid.java index 5530258c..6c63ee2e 100644 --- a/HIRS_Utils/src/main/java/hirs/utils/tpm/eventlog/uefi/UefiGuid.java +++ b/HIRS_Utils/src/main/java/hirs/utils/tpm/eventlog/uefi/UefiGuid.java @@ -23,8 +23,8 @@ public class UefiGuid { */ private static final int UUID_EPOCH_DIVISOR = 10000; - private static final Path JSON_PATH = FileSystems.getDefault().getPath("/opt", - "hirs", "default-properties", "vendor-table.json"); + private static final Path JSON_PATH = FileSystems.getDefault().getPath("/etc", + "hirs", "aca", "default-properties", "vendor-table.json"); private JsonObject uefiVendorRef; /** * guid byte array. @@ -175,10 +175,7 @@ public class UefiGuid { * @return true if the uuid is the Empty UUID, false if not */ public boolean isUnknownUUID() { - if (getVendorTableReference().equals("Unknown GUID reference")) { - return true; - } - return false; + return getVendorTableReference().equals("Unknown GUID reference"); } /** From 4de125c0f81a6d278c98c1b2e8996d03a549cddd Mon Sep 17 00:00:00 2001 From: Cyrus <24922493+cyrus-dev@users.noreply.github.com> Date: Tue, 3 Oct 2023 09:53:48 -0400 Subject: [PATCH 09/15] After some code review, there are changes and removals for the provisioning process. IdentityRequest is an old structure for the provisioner and it has been removed and some preliminary code file renames. --- .../AttestationCertificateAuthority.java | 20 +- ...estfulAttestationCertificateAuthority.java | 22 -- .../persist/RestfulInterface.java | 2 - .../persist/enums/AppraisalStatus.java | 1 - ...estHandler.java => AbstractProcessor.java} | 6 +- ....java => CertificateRequestProcessor.java} | 16 +- ...ndler.java => IdentityClaimProcessor.java} | 4 +- .../provision/IdentityRequestHandler.java | 345 ------------------ .../service/SupplyChainValidationService.java | 19 +- ...ionManager.java => ValidationService.java} | 2 +- .../validation/FirmwareScvValidator.java | 7 +- .../CertificatePageController.java | 4 +- ...eferenceManifestDetailsPageController.java | 4 +- 13 files changed, 36 insertions(+), 416 deletions(-) rename HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/{AbstractRequestHandler.java => AbstractProcessor.java} (98%) rename HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/{CertificateRequestHandler.java => CertificateRequestProcessor.java} (94%) rename HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/{IdentityClaimHandler.java => IdentityClaimProcessor.java} (99%) delete mode 100644 HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/IdentityRequestHandler.java rename HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/service/{ValidationManager.java => ValidationService.java} (99%) diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/AttestationCertificateAuthority.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/AttestationCertificateAuthority.java index c480a7b9..ede7b733 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/AttestationCertificateAuthority.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/AttestationCertificateAuthority.java @@ -8,9 +8,8 @@ import hirs.attestationca.persist.entity.manager.PolicyRepository; import hirs.attestationca.persist.entity.manager.ReferenceDigestValueRepository; import hirs.attestationca.persist.entity.manager.ReferenceManifestRepository; import hirs.attestationca.persist.entity.manager.TPM2ProvisionerStateRepository; -import hirs.attestationca.persist.provision.CertificateRequestHandler; -import hirs.attestationca.persist.provision.IdentityClaimHandler; -import hirs.attestationca.persist.provision.IdentityRequestHandler; +import hirs.attestationca.persist.provision.CertificateRequestProcessor; +import hirs.attestationca.persist.provision.IdentityClaimProcessor; import hirs.attestationca.persist.service.SupplyChainValidationService; import hirs.structs.converters.StructConverter; import lombok.extern.log4j.Log4j2; @@ -62,9 +61,8 @@ public abstract class AttestationCertificateAuthority { private final PolicyRepository policyRepository; private final TPM2ProvisionerStateRepository tpm2ProvisionerStateRepository; - private CertificateRequestHandler certificateRequestHandler; - private IdentityClaimHandler identityClaimHandler; - private IdentityRequestHandler identityRequestHandler; + private CertificateRequestProcessor certificateRequestHandler; + private IdentityClaimProcessor identityClaimHandler; /** * Constructor. @@ -109,19 +107,13 @@ public abstract class AttestationCertificateAuthority { this.policyRepository = policyRepository; this.tpm2ProvisionerStateRepository = tpm2ProvisionerStateRepository; - this.certificateRequestHandler = new CertificateRequestHandler(supplyChainValidationService, + this.certificateRequestHandler = new CertificateRequestProcessor(supplyChainValidationService, certificateRepository, deviceRepository, privateKey, acaCertificate, validDays, tpm2ProvisionerStateRepository); - this.identityClaimHandler = new IdentityClaimHandler(supplyChainValidationService, + this.identityClaimHandler = new IdentityClaimProcessor(supplyChainValidationService, certificateRepository, referenceManifestRepository, referenceDigestValueRepository, deviceRepository, tpm2ProvisionerStateRepository, policyRepository); - this.identityRequestHandler = new IdentityRequestHandler(structConverter, certificateRepository, - deviceRepository, supplyChainValidationService, privateKey, validDays, acaCertificate); - } - - byte[] processIdentityRequest(final byte[] identityRequest) { - return this.identityRequestHandler.processIdentityRequest(identityRequest); } byte[] processIdentityClaimTpm2(final byte[] identityClaim) { diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/RestfulAttestationCertificateAuthority.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/RestfulAttestationCertificateAuthority.java index ced6614d..18bb0458 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/RestfulAttestationCertificateAuthority.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/RestfulAttestationCertificateAuthority.java @@ -71,28 +71,6 @@ public class RestfulAttestationCertificateAuthority extends AttestationCertifica referenceDigestValueRepository, policyRepository, tpm2ProvisionerStateRepository); } - /** - * Processes a given IdentityRequestEnvelope and - * generates a IdentityResponseEnvelope. In most cases, - * a client will generate the request using the TPM "Collate Identity" process. - * - * Wrap the {@link AttestationCertificateAuthority#processIdentityRequest(byte[])} - * with a Spring {@link org.springframework.web.bind.annotation.RequestMapping}. Effectively, this method then will allow spring to - * serialize and deserialize the request and responses on method invocation and - * return, respectively. - * - * @param identityRequest generated during the collate identity process with a Tpm - * @return response for the request - */ - @Override - @ResponseBody - @RequestMapping(value = "/identity-request/process", - method = RequestMethod.POST, - consumes = MediaType.APPLICATION_OCTET_STREAM_VALUE) - public byte[] processIdentityRequest(@RequestBody final byte[] identityRequest) { - return super.processIdentityRequest(identityRequest); - } - /** * Listener for identity requests from TPM 2.0 provisioning. * diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/RestfulInterface.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/RestfulInterface.java index 10e63b9f..e6c8eeaf 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/RestfulInterface.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/RestfulInterface.java @@ -5,8 +5,6 @@ package hirs.attestationca.persist; */ public interface RestfulInterface { - byte[] processIdentityRequest(byte[] identityRequest); - byte[] processIdentityClaimTpm2(byte[] identityClaim); byte[] processCertificateRequest(byte[] certificateRequest); diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/enums/AppraisalStatus.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/enums/AppraisalStatus.java index 37431f5f..0988656c 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/enums/AppraisalStatus.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/enums/AppraisalStatus.java @@ -60,5 +60,4 @@ public class AppraisalStatus { this.message = message; this.additionalInfo = additionalInfo; } - } diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/AbstractRequestHandler.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/AbstractProcessor.java similarity index 98% rename from HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/AbstractRequestHandler.java rename to HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/AbstractProcessor.java index c7c7e27c..cca14eca 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/AbstractRequestHandler.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/AbstractProcessor.java @@ -41,7 +41,7 @@ import java.util.List; @Log4j2 @NoArgsConstructor -public class AbstractRequestHandler { +public class AbstractProcessor { @Getter private int validDays; @@ -51,8 +51,8 @@ public class AbstractRequestHandler { @Getter private PolicyRepository policyRepository; - public AbstractRequestHandler(final PrivateKey privateKey, - final int validDays) { + public AbstractProcessor(final PrivateKey privateKey, + final int validDays) { this.privateKey = privateKey; this.validDays = validDays; } diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/CertificateRequestHandler.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/CertificateRequestProcessor.java similarity index 94% rename from HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/CertificateRequestHandler.java rename to HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/CertificateRequestProcessor.java index 5eb42b76..f3c59ad1 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/CertificateRequestHandler.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/CertificateRequestProcessor.java @@ -27,7 +27,7 @@ import java.security.interfaces.RSAPublicKey; import java.util.List; @Log4j2 -public class CertificateRequestHandler extends AbstractRequestHandler { +public class CertificateRequestProcessor extends AbstractProcessor { private SupplyChainValidationService supplyChainValidationService; private CertificateRepository certificateRepository; @@ -42,13 +42,13 @@ public class CertificateRequestHandler extends AbstractRequestHandler { * @param validDays int for the time in which a certificate is valid. * @param tpm2ProvisionerStateRepository db connector for provisioner state. */ - public CertificateRequestHandler(final SupplyChainValidationService supplyChainValidationService, - final CertificateRepository certificateRepository, - final DeviceRepository deviceRepository, - final PrivateKey privateKey, - final X509Certificate acaCertificate, - final int validDays, - final TPM2ProvisionerStateRepository tpm2ProvisionerStateRepository) { + public CertificateRequestProcessor(final SupplyChainValidationService supplyChainValidationService, + final CertificateRepository certificateRepository, + final DeviceRepository deviceRepository, + final PrivateKey privateKey, + final X509Certificate acaCertificate, + final int validDays, + final TPM2ProvisionerStateRepository tpm2ProvisionerStateRepository) { super(privateKey, validDays); this.supplyChainValidationService = supplyChainValidationService; this.certificateRepository = certificateRepository; diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/IdentityClaimHandler.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/IdentityClaimProcessor.java similarity index 99% rename from HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/IdentityClaimHandler.java rename to HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/IdentityClaimProcessor.java index 07fc0f52..d969114c 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/IdentityClaimHandler.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/IdentityClaimProcessor.java @@ -57,7 +57,7 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; @Log4j2 -public class IdentityClaimHandler extends AbstractRequestHandler { +public class IdentityClaimProcessor extends AbstractProcessor { private static final String PCR_QUOTE_MASK = "0,1,2,3,4,5,6,7,8,9,10,11,12,13," + "14,15,16,17,18,19,20,21,22,23"; @@ -78,7 +78,7 @@ public class IdentityClaimHandler extends AbstractRequestHandler { /** * Constructor */ - public IdentityClaimHandler( + public IdentityClaimProcessor( final SupplyChainValidationService supplyChainValidationService, final CertificateRepository certificateRepository, final ReferenceManifestRepository referenceManifestRepository, diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/IdentityRequestHandler.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/IdentityRequestHandler.java deleted file mode 100644 index b2188d56..00000000 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/IdentityRequestHandler.java +++ /dev/null @@ -1,345 +0,0 @@ -package hirs.attestationca.persist.provision; - -import hirs.attestationca.persist.entity.manager.CertificateRepository; -import hirs.attestationca.persist.entity.manager.DeviceRepository; -import hirs.attestationca.persist.entity.userdefined.Certificate; -import hirs.attestationca.persist.entity.userdefined.Device; -import hirs.attestationca.persist.entity.userdefined.SupplyChainValidationSummary; -import hirs.attestationca.persist.entity.userdefined.certificate.EndorsementCredential; -import hirs.attestationca.persist.entity.userdefined.certificate.PlatformCredential; -import hirs.attestationca.persist.entity.userdefined.report.DeviceInfoReport; -import hirs.attestationca.persist.enums.AppraisalStatus; -import hirs.attestationca.persist.exceptions.IdentityProcessingException; -import hirs.attestationca.persist.provision.helper.CredentialManagementHelper; -import hirs.attestationca.persist.provision.helper.ProvisionUtils; -import hirs.attestationca.persist.service.SupplyChainValidationService; -import hirs.structs.converters.SimpleStructBuilder; -import hirs.structs.converters.StructConverter; -import hirs.structs.elements.aca.IdentityRequestEnvelope; -import hirs.structs.elements.aca.IdentityResponseEnvelope; -import hirs.structs.elements.aca.SymmetricAttestation; -import hirs.structs.elements.tpm.EncryptionScheme; -import hirs.structs.elements.tpm.IdentityProof; -import hirs.structs.elements.tpm.IdentityRequest; -import hirs.structs.elements.tpm.SymmetricKey; -import hirs.structs.elements.tpm.SymmetricKeyParams; -import lombok.extern.log4j.Log4j2; -import org.apache.commons.lang3.ArrayUtils; -import org.apache.commons.lang3.SerializationUtils; - -import java.io.IOException; -import java.math.BigInteger; -import java.security.PrivateKey; -import java.security.PublicKey; -import java.security.cert.X509Certificate; -import java.util.LinkedList; -import java.util.List; - -@Log4j2 -public class IdentityRequestHandler extends AbstractRequestHandler { - - /** - * Container wired ACA private key. - */ - private final PrivateKey privateKey; - private int validDays; - private StructConverter structConverter; - private CertificateRepository certificateRepository; - private DeviceRepository deviceRepository; - private SupplyChainValidationService supplyChainValidationService; - private X509Certificate acaCertificate; - - /** - * Constructor. - * @param structConverter the struct converter - * @param certificateRepository - * @param deviceRepository - * @param supplyChainValidationService the supply chain service - * @param privateKey - * @param validDays int for the time in which a certificate is valid. - * @param acaCertificate object holding the x509 certificate - */ - public IdentityRequestHandler(final StructConverter structConverter, - final CertificateRepository certificateRepository, - final DeviceRepository deviceRepository, - final SupplyChainValidationService supplyChainValidationService, - final PrivateKey privateKey, - final int validDays, final X509Certificate acaCertificate) { - super(privateKey, validDays); - this.structConverter = structConverter; - this.certificateRepository = certificateRepository; - this.deviceRepository = deviceRepository; - this.supplyChainValidationService = supplyChainValidationService; - this.privateKey = privateKey; - this.acaCertificate = acaCertificate; - } - - /** - * Basic implementation of the ACA processIdentityRequest method. - * - * @param identityRequest cannot be null - * @return an identity response for the specified request - */ - public byte[] processIdentityRequest(final byte[] identityRequest) { - log.info("Identity Request Received..."); - if (ArrayUtils.isEmpty(identityRequest)) { - throw new IllegalArgumentException("The IdentityRequest sent by the client" - + " cannot be null or empty."); - } - - log.debug("received request to process identity request"); - - // translate the bytes into the challenge - IdentityRequestEnvelope challenge = - structConverter.convert(identityRequest, IdentityRequestEnvelope.class); - - byte[] identityProof = unwrapIdentityRequest(structConverter.convert(challenge.getRequest(), - IdentityRequest.class)); - // the decrypted symmetric blob should be in the format of an IdentityProof. Use the - // struct converter to generate it. - IdentityProof proof = structConverter.convert(identityProof, IdentityProof.class); - - // convert the credential into an actual key. - log.debug("assembling public endorsement key"); - PublicKey ekPublicKey = null; - - // attempt to find an endorsement credential to validate - EndorsementCredential endorsementCredential = null; - - // first check the identity request for the endorsement credential - byte[] ecBytesFromIdentityRequest = proof.getEndorsementCredential(); - if (ArrayUtils.isNotEmpty(ecBytesFromIdentityRequest)) { - endorsementCredential = CredentialManagementHelper.storeEndorsementCredential( - this.certificateRepository, ecBytesFromIdentityRequest, ""); - try { - BigInteger publicKeyModulus = Certificate.getPublicKeyModulus( - endorsementCredential.getX509Certificate()); - if (publicKeyModulus != null) { - ekPublicKey = ProvisionUtils.assemblePublicKey(publicKeyModulus.toByteArray()); - } else { - throw new IdentityProcessingException("TPM 1.2 Provisioning requires EK " - + "Credentials to be created with RSA"); - } - } catch (IOException ioEx) { - log.error("Could not retrieve the public key modulus from the EK cert"); - } - } else if (ArrayUtils.isNotEmpty(challenge.getEndorsementCredentialModulus())) { - log.warn("EKC was not in the identity proof from the client. Checking for uploads."); - // Check if the EC was uploaded - ekPublicKey = - ProvisionUtils.assemblePublicKey(new String(challenge.getEndorsementCredentialModulus())); - endorsementCredential = getEndorsementCredential(ekPublicKey); - } else { - log.warn("Zero-length endorsement credential received in identity request."); - } - - // get platform credential from the identity request - List platformCredentials = new LinkedList<>(); - byte[] pcBytesFromIdentityRequest = proof.getPlatformCredential(); - if (ArrayUtils.isNotEmpty(pcBytesFromIdentityRequest)) { - platformCredentials.add(CredentialManagementHelper.storePlatformCredential( - this.certificateRepository, pcBytesFromIdentityRequest)); - } else if (endorsementCredential != null) { - // if none in the identity request, look for uploaded platform credentials - log.warn("PC was not in the identity proof from the client. Checking for uploads."); - platformCredentials.addAll(getPlatformCredentials(endorsementCredential)); - } else { - // if none in the identity request, look for uploaded platform credentials - log.warn("Zero-length platform credential received in identity request."); - } - - log.debug("Processing serialized device info report structure of length {}", - challenge.getDeviceInfoReportLength()); - - DeviceInfoReport deviceInfoReport = (DeviceInfoReport) - SerializationUtils.deserialize(challenge.getDeviceInfoReport()); - - if (deviceInfoReport == null) { - log.error("Failed to deserialize Device Info Report"); - throw new IdentityProcessingException("Device Info Report failed to deserialize " - + "from Identity Request"); - } - - log.info("Processing Device Info Report"); - // store device and device info report. - String deviceName = deviceInfoReport.getNetworkInfo().getHostname(); - Device device = this.deviceRepository.findByName(deviceName); - device.setDeviceInfo(deviceInfoReport); - - // perform supply chain validation. Note: It's possible that this should be done earlier - // in this method. - SupplyChainValidationSummary summary = - supplyChainValidationService.validateSupplyChain(endorsementCredential, - platformCredentials, device); - - // update the validation result in the device - device.setSupplyChainValidationStatus(summary.getOverallValidationResult()); - deviceRepository.save(device); - // check if supply chain validation succeeded. - // If it did not, do not provide the IdentityResponseEnvelope - if (summary.getOverallValidationResult() == AppraisalStatus.Status.PASS) { - IdentityResponseEnvelope identityResponse = - generateIdentityResponseEnvelopeAndStoreIssuedCert(challenge, - ekPublicKey, endorsementCredential, platformCredentials, device); - - return structConverter.convert(identityResponse); - } else { - log.error("Supply chain validation did not succeed. Result is: " - + summary.getOverallValidationResult()); - return new byte[]{}; - } - } - - /** - * Given a successful supply chain validation, generate an Identity Response envelope and - * the issued certificate. The issued cert is stored in the database. The identity response - * envelope is returned, and sent back to the client using the struct converter. - * @param challenge the identity request envelope - * @param ekPublicKey the EK public key - * @param endorsementCredential the endorsement credential - * @param platformCredentials the set of platform credentials - * @param device the device associated - * @return the identity response envelope - */ - private IdentityResponseEnvelope generateIdentityResponseEnvelopeAndStoreIssuedCert( - final IdentityRequestEnvelope challenge, final PublicKey ekPublicKey, - final EndorsementCredential endorsementCredential, - final List platformCredentials, final Device device) { - // decrypt the asymmetric / symmetric blobs - log.debug("unwrapping identity request"); - byte[] identityProof = unwrapIdentityRequest( - structConverter.convert(challenge.getRequest(), IdentityRequest.class)); - - // the decrypted symmetric blob should be in the format of an IdentityProof. Use the - // struct converter to generate it. - IdentityProof proof = structConverter.convert(identityProof, IdentityProof.class); - - // generate a session key and convert to byte array - log.debug("generating symmetric key for response"); - SymmetricKey sessionKey = ProvisionUtils.generateSymmetricKey(); - - // generate the asymmetric contents for the identity response - log.debug("generating asymmetric contents for response"); - byte[] asymmetricContents = ProvisionUtils.generateAsymmetricContents( - structConverter.convert(proof.getIdentityKey()), - structConverter.convert(sessionKey), ekPublicKey); - - // generate the identity credential - log.debug("generating credential from identity proof"); - - // transform the public key struct into a public key - PublicKey publicKey = ProvisionUtils.assemblePublicKey(proof.getIdentityKey().getStorePubKey().getKey()); - X509Certificate credential = generateCredential(publicKey, endorsementCredential, - platformCredentials, device.getDeviceInfo() - .getNetworkInfo() - .getIpAddress() - .getHostName(), acaCertificate); - - // generate the attestation using the credential and the key for this session - log.debug("generating symmetric response"); - SymmetricAttestation attestation = ProvisionUtils.generateAttestation(credential, sessionKey); - - // construct the response with the both the asymmetric contents and the CA attestation - IdentityResponseEnvelope identityResponse = - new SimpleStructBuilder<>(IdentityResponseEnvelope.class) - .set("asymmetricContents", asymmetricContents) - .set("symmetricAttestation", attestation).build(); - - // save new attestation certificate - byte[] derEncodedAttestationCertificate = ProvisionUtils.getDerEncodedCertificate(credential); - saveAttestationCertificate(this.certificateRepository, derEncodedAttestationCertificate, - endorsementCredential, platformCredentials, device); - - return identityResponse; - } - - /** - * Unwraps a given identityRequest. That is to say, decrypt the asymmetric portion of a data - * structure to determine the method to decrypt the symmetric portion. - * - * @param request - * to be decrypted - * @return the decrypted symmetric portion of an identity request. - */ - private byte[] unwrapIdentityRequest(final IdentityRequest request) { - // in case the TPM did not specify the IV, it must be extracted from the symmetric blob. - // the IV will then be the the first block of the cipher text. - final byte[] iv; - SymmetricKeyParams symmetricKeyParams = request.getSymmetricAlgorithm(); - if (symmetricKeyParams != null && symmetricKeyParams.getParams() != null) { - iv = symmetricKeyParams.getParams().getIv(); - } else { - iv = ProvisionUtils.extractInitialValue(request); - } - - // determine the encryption scheme from the algorithm - EncryptionScheme asymmetricScheme = - EncryptionScheme.fromInt(request.getAsymmetricAlgorithm().getEncryptionScheme()); - - // decrypt the asymmetric blob - byte[] decryptedAsymmetricBlob = - ProvisionUtils.decryptAsymmetricBlob(request.getAsymmetricBlob(), asymmetricScheme, getPrivateKey()); - - // construct our symmetric key structure from the decrypted asymmetric blob - SymmetricKey symmetricKey = - structConverter.convert(decryptedAsymmetricBlob, SymmetricKey.class); - - byte[] decryptedSymmetricBlob = - ProvisionUtils.decryptSymmetricBlob(request.getSymmetricBlob(), symmetricKey.getKey(), iv, - "AES/CBC/PKCS5Padding"); - - // decrypt the symmetric blob - return decryptedSymmetricBlob; - } - - /** - * Gets the Endorsement Credential from the DB given the EK public key. - * @param ekPublicKey the EK public key - * @return the Endorsement credential, if found, otherwise null - */ - private EndorsementCredential getEndorsementCredential(final PublicKey ekPublicKey) { - log.debug("Searching for endorsement credential based on public key: " + ekPublicKey); - - if (ekPublicKey == null) { - throw new IllegalArgumentException("Cannot look up an EC given a null public key"); - } - - EndorsementCredential credential = null; - - try { - credential = certificateRepository.findByPublicKeyModulusHexValue(Certificate - .getPublicKeyModulus(ekPublicKey) - .toString()); - } catch (IOException ioEx) { - log.error("Could not extract public key modulus", ioEx); - } - - if (credential == null) { - log.warn("Unable to find endorsement credential for public key."); - } else { - log.debug("Endorsement credential found."); - } - - return credential; - } - - private List getPlatformCredentials(final EndorsementCredential ec) { - List credentials = null; - - if (ec == null) { - log.warn("Cannot look for platform credential(s). Endorsement credential was null."); - } else { - log.debug("Searching for platform credential(s) based on holder serial number: " - + ec.getSerialNumber()); - credentials = this.certificateRepository.getByHolderSerialNumber(ec.getSerialNumber()); - if (credentials == null || credentials.isEmpty()) { - log.warn("No platform credential(s) found"); - } else { - log.debug("Platform Credential(s) found: " + credentials.size()); - } - } - - return credentials; - } - -} diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/service/SupplyChainValidationService.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/service/SupplyChainValidationService.java index e9e44d77..9e6f5d4c 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/service/SupplyChainValidationService.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/service/SupplyChainValidationService.java @@ -113,7 +113,7 @@ public class SupplyChainValidationService { // Validate the Endorsement Credential if (getPolicySettings().isEcValidationEnabled()) { log.info("Beginning Endorsement Credential Validation..."); - validations.add(ValidationManager.evaluateEndorsementCredentialStatus(ec, this.caCredentialRepository, acceptExpiredCerts)); + validations.add(ValidationService.evaluateEndorsementCredentialStatus(ec, this.caCredentialRepository, acceptExpiredCerts)); // store the device with the credential if (ec != null) { ec.setDeviceId(device.getId()); @@ -131,8 +131,8 @@ public class SupplyChainValidationService { pcErrorMessage = "Platform credential(s) missing\n"; } else { for (PlatformCredential pc : pcs) { - KeyStore trustedCa = ValidationManager.getCaChain(pc, caCredentialRepository); - platformScv = ValidationManager.evaluatePlatformCredentialStatus( + KeyStore trustedCa = ValidationService.getCaChain(pc, caCredentialRepository); + platformScv = ValidationService.evaluatePlatformCredentialStatus( pc, trustedCa, acceptExpiredCerts); if (platformScv.getValidationResult() == AppraisalStatus.Status.FAIL) { @@ -147,6 +147,7 @@ public class SupplyChainValidationService { chkDeltas = true; deltaMapping.put(pc, null); } + pc.setEndorsementCredential(ec); pc.setDeviceId(device.getId()); pc.setDeviceName(device.getDeviceInfo().getNetworkInfo().getHostname()); this.certificateRepository.save(pc); @@ -196,7 +197,7 @@ public class SupplyChainValidationService { // need to check if there are deltas, if not then just verify // components of the base if (baseCredential == null) { - validations.add(ValidationManager.buildValidationRecord( + validations.add(ValidationService.buildValidationRecord( SupplyChainValidation.ValidationType.PLATFORM_CREDENTIAL, AppraisalStatus.Status.FAIL, "Base Platform credential missing." @@ -209,7 +210,7 @@ public class SupplyChainValidationService { while (it.hasNext()) { PlatformCredential pc = it.next(); if (pc != null && !pc.isPlatformBase()) { - attributeScv = ValidationManager.evaluateDeltaAttributesStatus( + attributeScv = ValidationService.evaluateDeltaAttributesStatus( pc, device.getDeviceInfo(), baseCredential, deltaMapping, certificateRepository); if (attributeScv.getValidationResult() == AppraisalStatus.Status.FAIL) { @@ -222,7 +223,7 @@ public class SupplyChainValidationService { aes.add(baseCredential); validations.remove(platformScv); // if there are no deltas, just check base credential - platformScv = ValidationManager.evaluatePCAttributesStatus( + platformScv = ValidationService.evaluatePCAttributesStatus( baseCredential, device.getDeviceInfo(), ec, certificateRepository, componentResultRepository); validations.add(new SupplyChainValidation( @@ -243,7 +244,7 @@ public class SupplyChainValidationService { log.info("Beginning Firmware Validation..."); // may need to associated with device to pull the correct info // compare tpm quote with what is pulled from RIM associated file - validations.add(ValidationManager.evaluateFirmwareStatus(device, getPolicySettings(), + validations.add(ValidationService.evaluateFirmwareStatus(device, getPolicySettings(), referenceManifestRepository, referenceDigestValueRepository, caCredentialRepository)); } @@ -329,7 +330,7 @@ public class SupplyChainValidationService { log.error(ex); } - quoteScv = ValidationManager.buildValidationRecord(SupplyChainValidation + quoteScv = ValidationService.buildValidationRecord(SupplyChainValidation .ValidationType.FIRMWARE, fwStatus.getAppStatus(), fwStatus.getMessage(), eventLog, level); @@ -339,7 +340,7 @@ public class SupplyChainValidationService { = this.supplyChainValidationSummaryRepository.findByDevice(deviceName); for (SupplyChainValidation scv : previous.getValidations()) { if (scv.getValidationType() != SupplyChainValidation.ValidationType.FIRMWARE) { - validations.add(ValidationManager.buildValidationRecord(scv.getValidationType(), + validations.add(ValidationService.buildValidationRecord(scv.getValidationType(), scv.getValidationResult(), scv.getMessage(), scv.getCertificatesUsed().get(0), Level.INFO)); } diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/service/ValidationManager.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/service/ValidationService.java similarity index 99% rename from HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/service/ValidationManager.java rename to HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/service/ValidationService.java index f1dbaeee..1fe060b5 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/service/ValidationManager.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/service/ValidationService.java @@ -38,7 +38,7 @@ import java.util.Map; import java.util.Set; @Log4j2 -public class ValidationManager { +public class ValidationService { public static SupplyChainValidation evaluateEndorsementCredentialStatus( final EndorsementCredential ec, diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/validation/FirmwareScvValidator.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/validation/FirmwareScvValidator.java index fb1778c0..6c07353c 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/validation/FirmwareScvValidator.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/validation/FirmwareScvValidator.java @@ -1,24 +1,21 @@ package hirs.attestationca.persist.validation; import hirs.attestationca.persist.entity.manager.CACredentialRepository; -import hirs.attestationca.persist.entity.manager.CertificateRepository; import hirs.attestationca.persist.entity.manager.ReferenceDigestValueRepository; import hirs.attestationca.persist.entity.manager.ReferenceManifestRepository; import hirs.attestationca.persist.entity.userdefined.Device; import hirs.attestationca.persist.entity.userdefined.PolicySettings; import hirs.attestationca.persist.entity.userdefined.ReferenceManifest; -import hirs.attestationca.persist.entity.userdefined.SupplyChainValidation; import hirs.attestationca.persist.entity.userdefined.certificate.CertificateAuthorityCredential; import hirs.attestationca.persist.entity.userdefined.rim.BaseReferenceManifest; import hirs.attestationca.persist.entity.userdefined.rim.EventLogMeasurements; import hirs.attestationca.persist.entity.userdefined.rim.ReferenceDigestValue; import hirs.attestationca.persist.enums.AppraisalStatus; -import hirs.attestationca.persist.service.ValidationManager; +import hirs.attestationca.persist.service.ValidationService; import hirs.utils.SwidResource; import hirs.utils.tpm.eventlog.TCGEventLog; import hirs.utils.tpm.eventlog.TpmPcrEvent; import lombok.extern.log4j.Log4j2; -import org.apache.logging.log4j.Level; import java.io.IOException; import java.security.KeyStore; @@ -99,7 +96,7 @@ public class FirmwareScvValidator extends SupplyChainCredentialValidator { CertificateAuthorityCredential signingCert = null; for (CertificateAuthorityCredential cert : allCerts) { signingCert = cert; - KeyStore keyStore = ValidationManager.getCaChain(signingCert, + KeyStore keyStore = ValidationService.getCaChain(signingCert, caCredentialRepository); if (referenceManifestValidator.validateXmlSignature(signingCert)) { try { diff --git a/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/CertificatePageController.java b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/CertificatePageController.java index 60a81689..7a58df69 100644 --- a/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/CertificatePageController.java +++ b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/CertificatePageController.java @@ -389,14 +389,14 @@ public class CertificatePageController extends PageController { for (PlatformCredential pc : sharedCertificates) { if (!pc.isPlatformBase()) { pc.archive(); - certificateRepository.delete(pc); + certificateRepository.save(pc); } } } } certificate.archive(); - certificateRepository.delete(certificate); + certificateRepository.save(certificate); String deleteCompletedMessage = "Certificate successfully deleted"; messages.addInfo(deleteCompletedMessage); diff --git a/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/ReferenceManifestDetailsPageController.java b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/ReferenceManifestDetailsPageController.java index cd07a8a1..5d1cddba 100644 --- a/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/ReferenceManifestDetailsPageController.java +++ b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/ReferenceManifestDetailsPageController.java @@ -11,7 +11,7 @@ import hirs.attestationca.persist.entity.userdefined.rim.BaseReferenceManifest; import hirs.attestationca.persist.entity.userdefined.rim.EventLogMeasurements; import hirs.attestationca.persist.entity.userdefined.rim.ReferenceDigestValue; import hirs.attestationca.persist.entity.userdefined.rim.SupportReferenceManifest; -import hirs.attestationca.persist.service.ValidationManager; +import hirs.attestationca.persist.service.ValidationService; import hirs.attestationca.persist.validation.ReferenceManifestValidator; import hirs.attestationca.persist.validation.SupplyChainCredentialValidator; import hirs.attestationca.persist.validation.SupplyChainValidatorException; @@ -298,7 +298,7 @@ public class ReferenceManifestDetailsPageController extends PageController Date: Thu, 5 Oct 2023 10:54:25 -0400 Subject: [PATCH 10/15] Minor changes, mostly syntax and log message removals --- .../validation/CredentialValidator.java | 20 +++---------------- ...eferenceManifestDetailsPageController.java | 15 ++++++++------ 2 files changed, 12 insertions(+), 23 deletions(-) diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/validation/CredentialValidator.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/validation/CredentialValidator.java index be48a5e7..5fb76a7c 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/validation/CredentialValidator.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/validation/CredentialValidator.java @@ -38,12 +38,10 @@ public class CredentialValidator extends SupplyChainCredentialValidator { String message; if (ec == null) { message = baseErrorMessage + "an endorsement credential"; - log.error(message); return new AppraisalStatus(FAIL, message); } if (trustStore == null) { message = baseErrorMessage + "a trust store"; - log.error(message); return new AppraisalStatus(FAIL, message); } @@ -56,7 +54,6 @@ public class CredentialValidator extends SupplyChainCredentialValidator { if (keyInStore) { message = baseErrorMessage + "keys in the trust store"; - log.error(message); return new AppraisalStatus(FAIL, message); } @@ -77,19 +74,15 @@ public class CredentialValidator extends SupplyChainCredentialValidator { } } catch (IOException e) { message = "Couldn't retrieve X509 certificate from endorsement credential"; - log.error(message, e); return new AppraisalStatus(ERROR, message + " " + e.getMessage()); } catch (SupplyChainValidatorException e) { message = "An error occurred indicating the credential is not valid"; - log.warn(message, e); return new AppraisalStatus(ERROR, message + " " + e.getMessage()); } catch (CertificateExpiredException e) { message = "The endorsement credential is expired"; - log.warn(message, e); return new AppraisalStatus(FAIL, message + " " + e.getMessage()); } catch (CertificateNotYetValidException e) { message = "The endorsement credential is not yet valid"; - log.warn(message, e); return new AppraisalStatus(FAIL, message + " " + e.getMessage()); } } @@ -110,18 +103,15 @@ public class CredentialValidator extends SupplyChainCredentialValidator { String certVerifyMsg; if (pc == null) { message = baseErrorMessage + "a platform credential"; - log.error(message); return new AppraisalStatus(FAIL, message); } try { if (trustStore == null || trustStore.size() == 0) { message = baseErrorMessage + "an Issuer Cert in the Trust Store"; - log.error(message); return new AppraisalStatus(FAIL, message); } } catch (KeyStoreException e) { message = baseErrorMessage + "an initialized trust store"; - log.error(message); return new AppraisalStatus(FAIL, message); } @@ -139,7 +129,7 @@ public class CredentialValidator extends SupplyChainCredentialValidator { if (!acceptExpired && !pc.isValidOn(new Date())) { message = "Platform credential has expired"; // if not valid at the current time - log.warn(message); + log.debug(message); return new AppraisalStatus(FAIL, message); } @@ -148,12 +138,12 @@ public class CredentialValidator extends SupplyChainCredentialValidator { certVerifyMsg = verifyCertificate(attributeCert, trustStore); if (certVerifyMsg.isEmpty()) { message = PLATFORM_VALID; - log.info(message); + log.debug(message); return new AppraisalStatus(PASS, message); } else { message = String.format("Platform credential failed verification%n%s", certVerifyMsg); - log.error(message); + log.debug(message); return new AppraisalStatus(FAIL, message); } } catch (SupplyChainValidatorException scvEx) { @@ -180,17 +170,14 @@ public class CredentialValidator extends SupplyChainCredentialValidator { String message; if (platformCredential == null) { message = baseErrorMessage + "a platform credential"; - log.error(message); return new AppraisalStatus(FAIL, message); } if (deviceInfoReport == null) { message = baseErrorMessage + "a device info report"; - log.error(message); return new AppraisalStatus(FAIL, message); } if (endorsementCredential == null) { message = baseErrorMessage + "an endorsement credential"; - log.error(message); return new AppraisalStatus(FAIL, message); } @@ -199,7 +186,6 @@ public class CredentialValidator extends SupplyChainCredentialValidator { .equals(platformCredential.getHolderSerialNumber())) { message = "Platform Credential holder serial number does not match " + "the Endorsement Credential's serial number"; - log.error(message); return new AppraisalStatus(FAIL, message); } diff --git a/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/ReferenceManifestDetailsPageController.java b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/ReferenceManifestDetailsPageController.java index 5d1cddba..a86c48ef 100644 --- a/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/ReferenceManifestDetailsPageController.java +++ b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/ReferenceManifestDetailsPageController.java @@ -32,6 +32,7 @@ import org.springframework.web.servlet.ModelAndView; import java.io.IOException; import java.security.KeyStore; import java.security.NoSuchAlgorithmException; +import java.security.PublicKey; import java.security.cert.CertificateException; import java.util.ArrayList; import java.util.Arrays; @@ -306,17 +307,19 @@ public class ReferenceManifestDetailsPageController extends PageController Date: Fri, 6 Oct 2023 15:07:12 -0400 Subject: [PATCH 11/15] Some additional changes to fix UI errors after I was able to test against another machine. --- .../entity/tpm/TPM2ProvisionerState.java | 4 +-- .../provision/IdentityClaimProcessor.java | 4 +-- .../portal/PersistenceJPAConfig.java | 2 +- .../CertificatePageController.java | 21 ++++++----- .../controllers/DevicePageController.java | 4 +-- .../ReferenceManifestPageController.java | 6 ++-- ...estfulAttestationCertificateAuthority.java | 35 ------------------- .../RimDatabasePageController.java | 4 +-- 8 files changed, 26 insertions(+), 54 deletions(-) delete mode 100644 HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/RestfulAttestationCertificateAuthority.java diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/tpm/TPM2ProvisionerState.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/tpm/TPM2ProvisionerState.java index 18b50f08..fc44115d 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/tpm/TPM2ProvisionerState.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/tpm/TPM2ProvisionerState.java @@ -20,7 +20,7 @@ import java.util.Date; @NoArgsConstructor @Entity public class TPM2ProvisionerState { - private static final int MAX_BLOB_SIZE = 65535; + private static final int MAX_BLOB_SIZE = 16777215; @Id private Long firstPartOfNonce; @@ -88,7 +88,7 @@ public class TPM2ProvisionerState { /** * Convenience method for finding the {@link TPM2ProvisionerState} associated with the nonce. * - * @param TPM2ProvisionerStateRepository the {@link TPM2ProvisionerStateRepository} to use when looking for the + * @param tpm2ProvisionerStateRepository the {@link TPM2ProvisionerStateRepository} to use when looking for the * {@link TPM2ProvisionerState} * @param nonce the nonce to use as the key for the {@link TPM2ProvisionerState} * @return the {@link TPM2ProvisionerState} associated with the nonce; diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/IdentityClaimProcessor.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/IdentityClaimProcessor.java index d969114c..05e1ad77 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/IdentityClaimProcessor.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/IdentityClaimProcessor.java @@ -464,8 +464,8 @@ public class IdentityClaimProcessor extends AbstractProcessor { if (baseRim != null) { // pull the base versions of the swidtag and rimel and set the // event log hash for use during provision - SupportReferenceManifest sBaseRim = (SupportReferenceManifest) referenceManifestRepository - .findByBase64Hash(baseRim.getBase64Hash()); + SupportReferenceManifest sBaseRim = referenceManifestRepository + .getSupportRimEntityById(baseRim.getAssociatedRim()); baseRim.setEventLogHash(temp.getHexDecHash()); sBaseRim.setEventLogHash(temp.getHexDecHash()); referenceManifestRepository.save(baseRim); diff --git a/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/PersistenceJPAConfig.java b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/PersistenceJPAConfig.java index 8168d623..1bbac7ff 100644 --- a/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/PersistenceJPAConfig.java +++ b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/PersistenceJPAConfig.java @@ -52,7 +52,7 @@ import java.util.Properties; @PropertySource(value = "classpath:hibernate.properties"), // detects if file exists, if not, ignore errors - @PropertySource(value = "file:/etc/hirs/aca/application.properties", + @PropertySource(value = "file:/etc/hirs/aca/aca.properties", ignoreResourceNotFound = true) }) @ComponentScan({"hirs.attestationca.portal", "hirs.attestationca.portal.page.controllers", "hirs.attestationca.persist", "hirs.attestationca.persist.entity", "hirs.attestationca.persist.service"}) diff --git a/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/CertificatePageController.java b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/CertificatePageController.java index 7a58df69..7e71de79 100644 --- a/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/CertificatePageController.java +++ b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/CertificatePageController.java @@ -228,6 +228,7 @@ public class CertificatePageController extends PageController { }; int currentPage = input.getStart() / input.getLength(); + int itemCount = 0; Pageable paging = PageRequest.of(currentPage, input.getLength(), Sort.by(orderColumnName)); // special parsing for platform credential @@ -242,9 +243,10 @@ public class CertificatePageController extends PageController { org.springframework.data.domain.Page pagedResult = this.platformCertificateRepository.findAll(paging); if (pagedResult.hasContent()) { records.addAll(pagedResult.getContent()); + itemCount = records.size(); } - records.setRecordsTotal(input.getLength()); - records.setRecordsFiltered(platformCertificateRepository.count()); + records.setRecordsTotal(platformCertificateRepository.count()); + records.setRecordsFiltered(itemCount); EndorsementCredential associatedEC; if (!records.isEmpty()) { @@ -274,10 +276,11 @@ public class CertificatePageController extends PageController { org.springframework.data.domain.Page pagedResult = this.endorsementCredentialRepository.findAll(paging); if (pagedResult.hasContent()) { records.addAll(pagedResult.getContent()); + itemCount = records.size(); } - records.setRecordsTotal(input.getLength()); - records.setRecordsFiltered(endorsementCredentialRepository.count()); + records.setRecordsTotal(endorsementCredentialRepository.count()); + records.setRecordsFiltered(itemCount); log.debug("Returning list of size: " + records.size()); return new DataTableResponse<>(records, input); @@ -290,9 +293,10 @@ public class CertificatePageController extends PageController { if (pagedResult.hasContent()) { records.addAll(pagedResult.getContent()); + itemCount = records.size(); } - records.setRecordsTotal(input.getLength()); - records.setRecordsFiltered(caCredentialRepository.count()); + records.setRecordsTotal(caCredentialRepository.count()); + records.setRecordsFiltered(itemCount); log.debug("Returning list of size: " + records.size()); return new DataTableResponse<>(records, input); @@ -301,9 +305,10 @@ public class CertificatePageController extends PageController { org.springframework.data.domain.Page pagedResult = this.issuedCertificateRepository.findAll(paging); if (pagedResult.hasContent()) { records.addAll(pagedResult.getContent()); + itemCount = records.size(); } - records.setRecordsTotal(input.getLength()); - records.setRecordsFiltered(issuedCertificateRepository.count()); + records.setRecordsTotal(issuedCertificateRepository.count()); + records.setRecordsFiltered(itemCount); log.debug("Returning list of size: " + records.size()); return new DataTableResponse<>(records, input); diff --git a/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/DevicePageController.java b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/DevicePageController.java index 693e85a2..9153f21f 100644 --- a/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/DevicePageController.java +++ b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/DevicePageController.java @@ -96,8 +96,8 @@ public class DevicePageController extends PageController { if (pagedResult.hasContent()) { deviceList.addAll(pagedResult.getContent()); } - deviceList.setRecordsTotal(input.getLength()); - deviceList.setRecordsFiltered(deviceRepository.count()); + deviceList.setRecordsTotal(deviceRepository.count()); + deviceList.setRecordsFiltered(deviceList.size()); FilteredRecordsList> records = retrieveDevicesAndAssociatedCertificates(deviceList); diff --git a/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/ReferenceManifestPageController.java b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/ReferenceManifestPageController.java index 6a83348f..49690f99 100644 --- a/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/ReferenceManifestPageController.java +++ b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/ReferenceManifestPageController.java @@ -119,6 +119,7 @@ public class ReferenceManifestPageController extends PageController records = new FilteredRecordsList<>(); + int itemCount = 0; int currentPage = input.getStart() / input.getLength(); Pageable paging = PageRequest.of(currentPage, input.getLength(), Sort.by(orderColumnName)); org.springframework.data.domain.Page pagedResult = referenceManifestRepository.findAll(paging); @@ -127,11 +128,12 @@ public class ReferenceManifestPageController extends PageController(records, input); diff --git a/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/RestfulAttestationCertificateAuthority.java b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/RestfulAttestationCertificateAuthority.java deleted file mode 100644 index 74213318..00000000 --- a/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/RestfulAttestationCertificateAuthority.java +++ /dev/null @@ -1,35 +0,0 @@ -package hirs.attestationca.portal.page.controllers; - -/** - * Restful implementation of the {@link }. - * Exposes the ACA methods as REST endpoints. - */ -//@RestController -//@RequestMapping("/") -public class RestfulAttestationCertificateAuthority { -// private final ReferenceManifestRepository referenceManifestRepository; -// private final ReferenceDigestValueRepository referenceDigestValueRepository; -// -// @Autowired -// public RestfulAttestationCertificateAuthority( -// final ReferenceManifestRepository referenceManifestRepository, -// final ReferenceDigestValueRepository referenceDigestValueRepository) { -// -// this.referenceManifestRepository = referenceManifestRepository; -// this.referenceDigestValueRepository = referenceDigestValueRepository; -// -// } -// -// -// @ResponseBody -// @RequestMapping(value = "/upload-swidtag", method = RequestMethod.POST, consumes = MediaType.APPLICATION_OCTET_STREAM_VALUE) -// public byte[] uploadSwidtag(@RequestBody final byte[] request) { -// return null; -// } -// -// @ResponseBody -// @RequestMapping(value = "/upload-rimel", method = RequestMethod.POST, consumes = MediaType.APPLICATION_OCTET_STREAM_VALUE) -// public byte[] uploadRimel(@RequestBody final byte[] request) { -// return null; -// } -} diff --git a/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/RimDatabasePageController.java b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/RimDatabasePageController.java index 0a2e76ed..474ce59e 100644 --- a/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/RimDatabasePageController.java +++ b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/RimDatabasePageController.java @@ -117,8 +117,8 @@ public class RimDatabasePageController extends PageController { if (pagedResult.hasContent()) { referenceDigestValues.addAll(pagedResult.getContent()); } - referenceDigestValues.setRecordsTotal(input.getLength()); - referenceDigestValues.setRecordsFiltered(referenceDigestValueRepository.count()); + referenceDigestValues.setRecordsTotal(referenceDigestValueRepository.count()); + referenceDigestValues.setRecordsFiltered(referenceDigestValues.size()); // FilteredRecordsList referenceDigestValues = // OrderedListQueryDataTableAdapter.getOrderedList( From ec39bf55a335e03cbbc9695a16849b83f6d1dee5 Mon Sep 17 00:00:00 2001 From: Cyrus <24922493+cyrus-dev@users.noreply.github.com> Date: Tue, 10 Oct 2023 13:31:45 -0400 Subject: [PATCH 12/15] Corrected an issue with the root CA looking itself causing an issue because the one root CA had an illegal character. --- .../persist/entity/manager/CACredentialRepository.java | 1 + .../portal/page/controllers/DevicePageController.java | 4 ++-- .../page/controllers/ReferenceManifestPageController.java | 6 ++---- .../page/controllers/RimDatabasePageController.java | 4 ++-- .../portal/page/utils/CertificateStringMapBuilder.java | 8 ++++---- .../src/main/java/hirs/utils/BouncyCastleUtils.java | 4 ++-- 6 files changed, 13 insertions(+), 14 deletions(-) diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/manager/CACredentialRepository.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/manager/CACredentialRepository.java index cd30f254..d3f3074f 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/manager/CACredentialRepository.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/manager/CACredentialRepository.java @@ -17,4 +17,5 @@ public interface CACredentialRepository extends JpaRepository findBySubject(String subject); List findBySubjectSorted(String subject); CertificateAuthorityCredential findBySubjectKeyIdentifier(byte[] subjectKeyIdentifier); + CertificateAuthorityCredential findBySubjectKeyIdString(String subjectKeyIdString); } diff --git a/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/DevicePageController.java b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/DevicePageController.java index 9153f21f..693e85a2 100644 --- a/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/DevicePageController.java +++ b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/DevicePageController.java @@ -96,8 +96,8 @@ public class DevicePageController extends PageController { if (pagedResult.hasContent()) { deviceList.addAll(pagedResult.getContent()); } - deviceList.setRecordsTotal(deviceRepository.count()); - deviceList.setRecordsFiltered(deviceList.size()); + deviceList.setRecordsTotal(input.getLength()); + deviceList.setRecordsFiltered(deviceRepository.count()); FilteredRecordsList> records = retrieveDevicesAndAssociatedCertificates(deviceList); diff --git a/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/ReferenceManifestPageController.java b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/ReferenceManifestPageController.java index 49690f99..6a83348f 100644 --- a/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/ReferenceManifestPageController.java +++ b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/ReferenceManifestPageController.java @@ -119,7 +119,6 @@ public class ReferenceManifestPageController extends PageController records = new FilteredRecordsList<>(); - int itemCount = 0; int currentPage = input.getStart() / input.getLength(); Pageable paging = PageRequest.of(currentPage, input.getLength(), Sort.by(orderColumnName)); org.springframework.data.domain.Page pagedResult = referenceManifestRepository.findAll(paging); @@ -128,12 +127,11 @@ public class ReferenceManifestPageController extends PageController(records, input); diff --git a/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/RimDatabasePageController.java b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/RimDatabasePageController.java index 474ce59e..0a2e76ed 100644 --- a/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/RimDatabasePageController.java +++ b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/RimDatabasePageController.java @@ -117,8 +117,8 @@ public class RimDatabasePageController extends PageController { if (pagedResult.hasContent()) { referenceDigestValues.addAll(pagedResult.getContent()); } - referenceDigestValues.setRecordsTotal(referenceDigestValueRepository.count()); - referenceDigestValues.setRecordsFiltered(referenceDigestValues.size()); + referenceDigestValues.setRecordsTotal(input.getLength()); + referenceDigestValues.setRecordsFiltered(referenceDigestValueRepository.count()); // FilteredRecordsList referenceDigestValues = // OrderedListQueryDataTableAdapter.getOrderedList( diff --git a/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/utils/CertificateStringMapBuilder.java b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/utils/CertificateStringMapBuilder.java index f7921b6a..9c1066a9 100644 --- a/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/utils/CertificateStringMapBuilder.java +++ b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/utils/CertificateStringMapBuilder.java @@ -20,6 +20,7 @@ import org.bouncycastle.util.encoders.Hex; import java.io.IOException; import java.math.BigInteger; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; @@ -149,15 +150,14 @@ public final class CertificateStringMapBuilder { final Certificate certificate, final CertificateRepository certificateRepository, final CACredentialRepository caCredentialRepository) { - List issuerCertificates = new LinkedList<>(); + List issuerCertificates = new ArrayList<>(); CertificateAuthorityCredential skiCA = null; String issuerResult; //Check if there is a subject organization if (certificate.getAuthorityKeyIdentifier() != null && !certificate.getAuthorityKeyIdentifier().isEmpty()) { - byte[] bytes = Hex.decode(certificate.getAuthorityKeyIdentifier()); - skiCA = caCredentialRepository.findBySubjectKeyIdentifier(bytes); + skiCA = caCredentialRepository.findBySubjectKeyIdString(certificate.getAuthorityKeyIdentifier()); } else { log.error(String.format("Certificate (%s) for %s has no authority key identifier.", certificate.getClass().toString(), certificate.getSubject())); @@ -185,7 +185,7 @@ public final class CertificateStringMapBuilder { if (issuerResult.isEmpty()) { //Check if it's root certificate if (BouncyCastleUtils.x500NameCompare(issuerCert.getIssuerSorted(), - issuerCert.getSubject())) { + issuerCert.getSubjectSorted())) { return null; } return containsAllChain(issuerCert, certificateRepository, caCredentialRepository); diff --git a/HIRS_Utils/src/main/java/hirs/utils/BouncyCastleUtils.java b/HIRS_Utils/src/main/java/hirs/utils/BouncyCastleUtils.java index 3c748284..eab8a820 100644 --- a/HIRS_Utils/src/main/java/hirs/utils/BouncyCastleUtils.java +++ b/HIRS_Utils/src/main/java/hirs/utils/BouncyCastleUtils.java @@ -36,8 +36,8 @@ public final class BouncyCastleUtils { X500Name x500Name2; try { - x500Name1 = new X500Name(nameValue1.replace(SEPARATOR_PLUS, SEPARATOR_COMMA)); - x500Name2 = new X500Name(nameValue2.replace(SEPARATOR_PLUS, SEPARATOR_COMMA)); + x500Name1 = new X500Name(nameValue1); + x500Name2 = new X500Name(nameValue2); result = x500Name1.equals(x500Name2); } catch (IllegalArgumentException iaEx) { log.error(iaEx.toString()); From 841080ba58dc8a4b0c124925b5b204aa71ed1572 Mon Sep 17 00:00:00 2001 From: Cyrus <24922493+cyrus-dev@users.noreply.github.com> Date: Tue, 10 Oct 2023 14:22:35 -0400 Subject: [PATCH 13/15] Missed some additional code for testing the element listing --- .../CertificatePageController.java | 29 +++++-------------- 1 file changed, 8 insertions(+), 21 deletions(-) diff --git a/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/CertificatePageController.java b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/CertificatePageController.java index 7e71de79..0cf5c1ec 100644 --- a/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/CertificatePageController.java +++ b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/CertificatePageController.java @@ -235,18 +235,14 @@ public class CertificatePageController extends PageController { // Add the EndorsementCredential for each PlatformCredential based on the // serial number. (pc.HolderSerialNumber = ec.SerialNumber) if (certificateType.equals(PLATFORMCREDENTIAL)) { -// records = OrderedListQueryDataTableAdapter.getOrderedList( -// getCertificateClass(certificateType), platformCertificateRepository, -// input, orderColumnName, criteriaModifier); FilteredRecordsList records = new FilteredRecordsList<>(); org.springframework.data.domain.Page pagedResult = this.platformCertificateRepository.findAll(paging); if (pagedResult.hasContent()) { records.addAll(pagedResult.getContent()); - itemCount = records.size(); } - records.setRecordsTotal(platformCertificateRepository.count()); - records.setRecordsFiltered(itemCount); + records.setRecordsTotal(input.getLength()); + records.setRecordsFiltered(platformCertificateRepository.count()); EndorsementCredential associatedEC; if (!records.isEmpty()) { @@ -269,34 +265,26 @@ public class CertificatePageController extends PageController { log.debug("Returning list of size: " + records.size()); return new DataTableResponse<>(records, input); } else if (certificateType.equals(ENDORSEMENTCREDENTIAL)) { -// records = OrderedListQueryDataTableAdapter.getOrderedList( -// getCertificateClass(certificateType), endorsementCredentialRepository, -// input, orderColumnName, criteriaModifier); FilteredRecordsList records = new FilteredRecordsList<>(); org.springframework.data.domain.Page pagedResult = this.endorsementCredentialRepository.findAll(paging); if (pagedResult.hasContent()) { records.addAll(pagedResult.getContent()); - itemCount = records.size(); } - records.setRecordsTotal(endorsementCredentialRepository.count()); - records.setRecordsFiltered(itemCount); + records.setRecordsTotal(input.getLength()); + records.setRecordsFiltered(endorsementCredentialRepository.count()); log.debug("Returning list of size: " + records.size()); return new DataTableResponse<>(records, input); } else if (certificateType.equals(TRUSTCHAIN)) { -// records = OrderedListQueryDataTableAdapter.getOrderedList( -// getCertificateClass(certificateType), caCredentialRepository, -// input, orderColumnName, criteriaModifier); FilteredRecordsList records = new FilteredRecordsList<>(); org.springframework.data.domain.Page pagedResult = this.caCredentialRepository.findAll(paging); if (pagedResult.hasContent()) { records.addAll(pagedResult.getContent()); - itemCount = records.size(); } - records.setRecordsTotal(caCredentialRepository.count()); - records.setRecordsFiltered(itemCount); + records.setRecordsTotal(input.getLength()); + records.setRecordsFiltered(caCredentialRepository.count()); log.debug("Returning list of size: " + records.size()); return new DataTableResponse<>(records, input); @@ -305,10 +293,9 @@ public class CertificatePageController extends PageController { org.springframework.data.domain.Page pagedResult = this.issuedCertificateRepository.findAll(paging); if (pagedResult.hasContent()) { records.addAll(pagedResult.getContent()); - itemCount = records.size(); } - records.setRecordsTotal(issuedCertificateRepository.count()); - records.setRecordsFiltered(itemCount); + records.setRecordsTotal(input.getLength()); + records.setRecordsFiltered(issuedCertificateRepository.count()); log.debug("Returning list of size: " + records.size()); return new DataTableResponse<>(records, input); From 0c131e6ee57ecab085ac7d383fe0ad2d53df7d8c Mon Sep 17 00:00:00 2001 From: Cyrus <24922493+cyrus-dev@users.noreply.github.com> Date: Tue, 10 Oct 2023 15:06:12 -0400 Subject: [PATCH 14/15] Fixed the inccorect data at the bottom of the data tables when displaying the elements. --- .../CertificatePageController.java | 24 ++++++++++++++----- 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/CertificatePageController.java b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/CertificatePageController.java index 0cf5c1ec..08df7d76 100644 --- a/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/CertificatePageController.java +++ b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/CertificatePageController.java @@ -228,7 +228,6 @@ public class CertificatePageController extends PageController { }; int currentPage = input.getStart() / input.getLength(); - int itemCount = 0; Pageable paging = PageRequest.of(currentPage, input.getLength(), Sort.by(orderColumnName)); // special parsing for platform credential @@ -236,12 +235,15 @@ public class CertificatePageController extends PageController { // serial number. (pc.HolderSerialNumber = ec.SerialNumber) if (certificateType.equals(PLATFORMCREDENTIAL)) { FilteredRecordsList records = new FilteredRecordsList<>(); - org.springframework.data.domain.Page pagedResult = this.platformCertificateRepository.findAll(paging); + if (pagedResult.hasContent()) { records.addAll(pagedResult.getContent()); + records.setRecordsTotal(pagedResult.getContent().size()); + } else { + records.setRecordsTotal(input.getLength()); } - records.setRecordsTotal(input.getLength()); + records.setRecordsFiltered(platformCertificateRepository.count()); EndorsementCredential associatedEC; @@ -267,11 +269,14 @@ public class CertificatePageController extends PageController { } else if (certificateType.equals(ENDORSEMENTCREDENTIAL)) { FilteredRecordsList records = new FilteredRecordsList<>(); org.springframework.data.domain.Page pagedResult = this.endorsementCredentialRepository.findAll(paging); + if (pagedResult.hasContent()) { records.addAll(pagedResult.getContent()); + records.setRecordsTotal(pagedResult.getContent().size()); + } else { + records.setRecordsTotal(input.getLength()); } - records.setRecordsTotal(input.getLength()); records.setRecordsFiltered(endorsementCredentialRepository.count()); log.debug("Returning list of size: " + records.size()); @@ -282,8 +287,11 @@ public class CertificatePageController extends PageController { if (pagedResult.hasContent()) { records.addAll(pagedResult.getContent()); + records.setRecordsTotal(pagedResult.getContent().size()); + } else { + records.setRecordsTotal(input.getLength()); } - records.setRecordsTotal(input.getLength()); + records.setRecordsFiltered(caCredentialRepository.count()); log.debug("Returning list of size: " + records.size()); @@ -291,10 +299,14 @@ public class CertificatePageController extends PageController { } else if (certificateType.equals(ISSUEDCERTIFICATES)) { FilteredRecordsList records = new FilteredRecordsList<>(); org.springframework.data.domain.Page pagedResult = this.issuedCertificateRepository.findAll(paging); + if (pagedResult.hasContent()) { records.addAll(pagedResult.getContent()); + records.setRecordsTotal(pagedResult.getContent().size()); + } else { + records.setRecordsTotal(input.getLength()); } - records.setRecordsTotal(input.getLength()); + records.setRecordsFiltered(issuedCertificateRepository.count()); log.debug("Returning list of size: " + records.size()); From b018429b698f3ec92c1c787739dab24ad5498161 Mon Sep 17 00:00:00 2001 From: Cyrus <24922493+cyrus-dev@users.noreply.github.com> Date: Fri, 13 Oct 2023 09:39:20 -0400 Subject: [PATCH 15/15] Additional code to correctly display # of elements --- .../portal/page/controllers/DevicePageController.java | 8 +++----- .../controllers/ReferenceManifestPageController.java | 8 ++++++-- .../page/controllers/RimDatabasePageController.java | 9 +++------ .../controllers/ValidationReportsPageController.java | 5 ++++- 4 files changed, 16 insertions(+), 14 deletions(-) diff --git a/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/DevicePageController.java b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/DevicePageController.java index 693e85a2..4b475c35 100644 --- a/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/DevicePageController.java +++ b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/DevicePageController.java @@ -84,10 +84,6 @@ public class DevicePageController extends PageController { // get all the devices FilteredRecordsList deviceList = new FilteredRecordsList<>(); -// OrderedListQueryDataTableAdapter.getOrderedList( -// Device.class, -// deviceRepository, -// input, orderColumnName); int currentPage = input.getStart() / input.getLength(); Pageable paging = PageRequest.of(currentPage, input.getLength(), Sort.by(orderColumnName)); @@ -95,8 +91,10 @@ public class DevicePageController extends PageController { if (pagedResult.hasContent()) { deviceList.addAll(pagedResult.getContent()); + deviceList.setRecordsTotal(pagedResult.getContent().size()); + } else { + deviceList.setRecordsTotal(input.getLength()); } - deviceList.setRecordsTotal(input.getLength()); deviceList.setRecordsFiltered(deviceRepository.count()); FilteredRecordsList> records diff --git a/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/ReferenceManifestPageController.java b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/ReferenceManifestPageController.java index 6a83348f..e9b53b74 100644 --- a/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/ReferenceManifestPageController.java +++ b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/ReferenceManifestPageController.java @@ -115,22 +115,26 @@ public class ReferenceManifestPageController extends PageController records = new FilteredRecordsList<>(); int currentPage = input.getStart() / input.getLength(); Pageable paging = PageRequest.of(currentPage, input.getLength(), Sort.by(orderColumnName)); org.springframework.data.domain.Page pagedResult = referenceManifestRepository.findAll(paging); + int rimCount = 0; if (pagedResult.hasContent()) { for (ReferenceManifest manifest : pagedResult.getContent()) { if (!manifest.getRimType().equals(ReferenceManifest.MEASUREMENT_RIM)) { records.add(manifest); + rimCount++; } } + records.setRecordsTotal(rimCount); + } else { + records.setRecordsTotal(input.getLength()); } - records.setRecordsTotal(input.getLength()); + records.setRecordsFiltered(referenceManifestRepository.count()); log.debug("Returning list of size: " + records.size()); diff --git a/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/RimDatabasePageController.java b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/RimDatabasePageController.java index 0a2e76ed..36cd752c 100644 --- a/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/RimDatabasePageController.java +++ b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/RimDatabasePageController.java @@ -116,15 +116,12 @@ public class RimDatabasePageController extends PageController { if (pagedResult.hasContent()) { referenceDigestValues.addAll(pagedResult.getContent()); + referenceDigestValues.setRecordsTotal(pagedResult.getContent().size()); + } else { + referenceDigestValues.setRecordsTotal(input.getLength()); } - referenceDigestValues.setRecordsTotal(input.getLength()); referenceDigestValues.setRecordsFiltered(referenceDigestValueRepository.count()); -// FilteredRecordsList referenceDigestValues = -// OrderedListQueryDataTableAdapter.getOrderedList( -// referenceDigestValueRepository, -// input, orderColumnName, criteriaModifier, entityManager); - // might be able to get rid of this, maybe right a query that looks for not updated SupportReferenceManifest support; for (ReferenceDigestValue rdv : referenceDigestValues) { diff --git a/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/ValidationReportsPageController.java b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/ValidationReportsPageController.java index e2a5f5db..a2a1ccec 100644 --- a/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/ValidationReportsPageController.java +++ b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/ValidationReportsPageController.java @@ -126,8 +126,11 @@ public class ValidationReportsPageController extends PageController(records, input);