From 17a8732908c07b8008ede6e1eba8816b39f57fde Mon Sep 17 00:00:00 2001 From: Cyrus <24922493+cyrus-dev@users.noreply.github.com> Date: Mon, 6 Mar 2023 20:56:34 -0500 Subject: [PATCH] added in finished files --- HIRS_AttestationCA/build.gradle | 52 + .../persist/PCRQuoteValidator.java | 223 ++++ .../persist/entity/AbstractEntity.java | 100 ++ .../persist/entity/Appraiser.java | 88 ++ .../persist/entity/ArchivableEntity.java | 95 ++ .../attestationca/persist/entity/Policy.java | 72 ++ .../persist/entity/UserDefinedEntity.java | 46 + .../entity/manager/CertificateRepository.java | 11 + .../entity/manager/DeviceRepository.java | 13 + .../manager/ReferenceManifestRepository.java | 11 + .../entity/manager/SettingsRepository.java | 12 + .../persist/entity/package-info.java | 4 + .../entity/userdefined/Certificate.java | 1076 +++++++++++++++++ .../persist/entity/userdefined/Device.java | 63 + .../userdefined/ReferenceDigestValue.java | 72 ++ .../entity/userdefined/ReferenceManifest.java | 156 +++ .../persist/entity/userdefined/Report.java | 41 + .../userdefined/SupplyChainSettings.java | 123 ++ .../CertificateAuthorityCredential.java | 151 +++ .../certificate/CertificateVariables.java | 47 + .../certificate/ConformanceCredential.java | 65 + .../DeviceAssociatedCertificate.java | 69 ++ .../certificate/EndorsementCredential.java | 652 ++++++++++ .../IssuedAttestationCertificate.java | 105 ++ .../certificate/PlatformCredential.java | 796 ++++++++++++ .../attributes/CommonCriteriaMeasures.java | 300 +++++ .../attributes/ComponentAddress.java | 91 ++ .../attributes/ComponentClass.java | 248 ++++ .../attributes/ComponentIdentifier.java | 238 ++++ .../certificate/attributes/FIPSLevel.java | 122 ++ .../attributes/PlatformConfiguration.java | 104 ++ .../attributes/PlatformConfigurationV1.java | 105 ++ .../attributes/PlatformProperty.java | 67 + .../attributes/TBBSecurityAssertion.java | 282 +++++ .../attributes/TPMSecurityAssertions.java | 121 ++ .../attributes/TPMSpecification.java | 58 + .../certificate/attributes/URIReference.java | 91 ++ .../attributes/V2/AttributeStatus.java | 40 + .../attributes/V2/CertificateIdentifier.java | 127 ++ .../attributes/V2/ComponentIdentifierV2.java | 251 ++++ .../V2/PlatformConfigurationV2.java | 119 ++ .../attributes/V2/PlatformPropertyV2.java | 100 ++ .../attributes/V2/package-info.java | 1 + .../certificate/attributes/package-info.java | 1 + .../userdefined/certificate/package-info.java | 1 + .../entity/userdefined/info/FirmwareInfo.java | 60 + .../entity/userdefined/info/HardwareInfo.java | 122 ++ .../entity/userdefined/info/NetworkInfo.java | 111 ++ .../entity/userdefined/info/OSInfo.java | 99 ++ .../entity/userdefined/info/RIMInfo.java | 66 + .../entity/userdefined/info/TPMInfo.java | 319 +++++ .../entity/userdefined/package-info.java | 1 + .../userdefined/report/DeviceInfoReport.java | 289 +++++ .../result/CertificateValidationResult.java | 51 + .../rim/BaseReferenceManifest.java | 390 ++++++ .../userdefined/rim/EventLogMeasurements.java | 64 + .../rim/SupportReferenceManifest.java | 114 ++ .../entity/userdefined/rim/package-info.java | 1 + .../persist/enums/AppraisalStatus.java | 106 ++ .../persist/enums/HealthStatus.java | 57 + .../persist/enums/package-info.java | 1 + .../service/CertificateServiceImpl.java | 20 + .../persist/service/DbServiceImpl.java | 15 + .../persist/service/DefaultService.java | 4 + .../persist/service/DeviceServiceImpl.java | 32 + .../service/ReferenceManifestServiceImpl.java | 70 ++ .../persist/service/SettingsServiceImpl.java | 41 + .../persist/type/InetAddressType.java | 190 +++ .../persist/type/X509CertificateType.java | 203 ++++ .../src/main/resources/component-class.json | 477 ++++++++ .../src/main/resources/vendor-table.json | 233 ++++ .../attestationca/portal/HIRSApplication.java | 36 + .../portal/HIRSDbInitializer.java | 59 + .../portal/PersistenceJPAConfig.java | 86 ++ .../portal/datatables/DataTableResponse.java | 74 ++ .../attestationca/portal/package-info.java | 4 + .../hirs/attestationca/portal/page/Page.java | 220 ++++ .../portal/page/PageController.java | 169 +++ .../portal/page/PageMessages.java | 70 ++ .../attestationca/portal/page/PageParams.java | 17 + .../portal/page/PolicyPageModel.java | 80 ++ .../CertificateDetailsPageController.java | 119 ++ .../CertificatePageController.java | 73 ++ .../controllers/DevicePageController.java | 60 + .../page/controllers/ErrorController.java | 23 + .../page/controllers/IndexPageController.java | 42 + .../controllers/LombokLoggingController.java | 21 + .../controllers/PolicyPageController.java | 962 +++++++++++++++ .../params/CertificateDetailsPageParams.java | 59 + .../portal/page/params/NoPageParams.java | 21 + .../ReferenceManifestDetailsPageParams.java | 39 + .../params/ReferenceManifestPageParams.java | 44 + .../portal/page/params/package-info.java | 1 + .../utils/CertificateStringMapBuilder.java | 170 +++ .../portal/page/utils/PciIds.java | 191 +++ .../portal/page/utils/package-info.java | 1 + .../src/main/resources/log4j2-spring.xml | 35 + 97 files changed, 12422 insertions(+) create mode 100644 HIRS_AttestationCA/build.gradle create mode 100644 HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/PCRQuoteValidator.java create mode 100644 HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/AbstractEntity.java create mode 100644 HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/Appraiser.java create mode 100644 HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/ArchivableEntity.java create mode 100644 HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/Policy.java create mode 100644 HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/UserDefinedEntity.java create mode 100644 HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/manager/CertificateRepository.java create mode 100644 HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/manager/DeviceRepository.java create mode 100644 HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/manager/ReferenceManifestRepository.java create mode 100644 HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/manager/SettingsRepository.java create mode 100644 HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/package-info.java create mode 100644 HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/Certificate.java create mode 100644 HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/Device.java create mode 100644 HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/ReferenceDigestValue.java create mode 100644 HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/ReferenceManifest.java create mode 100644 HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/Report.java create mode 100644 HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/SupplyChainSettings.java create mode 100644 HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/CertificateAuthorityCredential.java create mode 100644 HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/CertificateVariables.java create mode 100644 HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/ConformanceCredential.java create mode 100644 HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/DeviceAssociatedCertificate.java create mode 100644 HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/EndorsementCredential.java create mode 100644 HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/IssuedAttestationCertificate.java create mode 100644 HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/PlatformCredential.java create mode 100644 HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/attributes/CommonCriteriaMeasures.java create mode 100644 HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/attributes/ComponentAddress.java create mode 100644 HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/attributes/ComponentClass.java create mode 100644 HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/attributes/ComponentIdentifier.java create mode 100644 HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/attributes/FIPSLevel.java create mode 100644 HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/attributes/PlatformConfiguration.java create mode 100644 HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/attributes/PlatformConfigurationV1.java create mode 100644 HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/attributes/PlatformProperty.java create mode 100644 HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/attributes/TBBSecurityAssertion.java create mode 100644 HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/attributes/TPMSecurityAssertions.java create mode 100644 HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/attributes/TPMSpecification.java create mode 100644 HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/attributes/URIReference.java create mode 100644 HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/attributes/V2/AttributeStatus.java create mode 100644 HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/attributes/V2/CertificateIdentifier.java create mode 100644 HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/attributes/V2/ComponentIdentifierV2.java create mode 100644 HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/attributes/V2/PlatformConfigurationV2.java create mode 100644 HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/attributes/V2/PlatformPropertyV2.java create mode 100644 HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/attributes/V2/package-info.java create mode 100644 HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/attributes/package-info.java create mode 100644 HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/package-info.java create mode 100644 HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/info/FirmwareInfo.java create mode 100644 HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/info/HardwareInfo.java create mode 100644 HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/info/NetworkInfo.java create mode 100644 HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/info/OSInfo.java create mode 100644 HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/info/RIMInfo.java create mode 100644 HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/info/TPMInfo.java create mode 100644 HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/package-info.java create mode 100644 HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/report/DeviceInfoReport.java create mode 100644 HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/result/CertificateValidationResult.java create mode 100644 HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/rim/BaseReferenceManifest.java create mode 100644 HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/rim/EventLogMeasurements.java create mode 100644 HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/rim/SupportReferenceManifest.java create mode 100644 HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/rim/package-info.java create mode 100644 HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/enums/AppraisalStatus.java create mode 100644 HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/enums/HealthStatus.java create mode 100644 HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/enums/package-info.java create mode 100644 HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/service/CertificateServiceImpl.java create mode 100644 HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/service/DbServiceImpl.java create mode 100644 HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/service/DefaultService.java create mode 100644 HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/service/DeviceServiceImpl.java create mode 100644 HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/service/ReferenceManifestServiceImpl.java create mode 100644 HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/service/SettingsServiceImpl.java create mode 100644 HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/type/InetAddressType.java create mode 100644 HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/type/X509CertificateType.java create mode 100644 HIRS_AttestationCA/src/main/resources/component-class.json create mode 100644 HIRS_AttestationCA/src/main/resources/vendor-table.json create mode 100644 HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/HIRSApplication.java create mode 100644 HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/HIRSDbInitializer.java create mode 100644 HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/PersistenceJPAConfig.java create mode 100644 HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/datatables/DataTableResponse.java create mode 100644 HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/package-info.java create mode 100644 HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/Page.java create mode 100644 HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/PageController.java create mode 100644 HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/PageMessages.java create mode 100644 HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/PageParams.java create mode 100644 HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/PolicyPageModel.java create mode 100644 HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/CertificateDetailsPageController.java create mode 100644 HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/CertificatePageController.java create mode 100644 HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/DevicePageController.java create mode 100644 HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/ErrorController.java create mode 100644 HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/IndexPageController.java create mode 100644 HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/LombokLoggingController.java create mode 100644 HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/PolicyPageController.java create mode 100644 HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/params/CertificateDetailsPageParams.java create mode 100644 HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/params/NoPageParams.java create mode 100644 HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/params/ReferenceManifestDetailsPageParams.java create mode 100644 HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/params/ReferenceManifestPageParams.java create mode 100644 HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/params/package-info.java create mode 100644 HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/utils/CertificateStringMapBuilder.java create mode 100644 HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/utils/PciIds.java create mode 100644 HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/utils/package-info.java create mode 100644 HIRS_AttestationCAPortal/src/main/resources/log4j2-spring.xml diff --git a/HIRS_AttestationCA/build.gradle b/HIRS_AttestationCA/build.gradle new file mode 100644 index 00000000..1b68e194 --- /dev/null +++ b/HIRS_AttestationCA/build.gradle @@ -0,0 +1,52 @@ +plugins { + id 'java' + id 'io.spring.dependency-management' version '1.1.0' +} + +java { + toolchain { + languageVersion = JavaLanguageVersion.of(17) + } +} + +repositories { + mavenCentral() + + flatDir { dirs "lib" } +} + +configurations { + compileOnly { + extendsFrom annotationProcessor + } +} + +dependencies { + implementation project(':HIRS_Utils') + + implementation 'org.springframework.boot:spring-boot-starter-data-jpa:3.0.1' + + implementation 'commons-codec:commons-codec:1.15' + implementation 'com.eclipsesource.minimal-json:minimal-json:0.9.5' + implementation 'com.google.guava:guava:31.1-jre' + implementation 'org.apache.commons:commons-lang3:3.12.0' + + implementation 'org.bouncycastle:bcmail-jdk15on:1.70' + implementation 'jakarta.persistence:jakarta.persistence-api:3.1.0' +// implementation libs.jackson_core +// implementation libs.jackson_databind + implementation 'com.fasterxml.jackson.core:jackson-core:2.14.2' + implementation 'com.fasterxml.jackson.core:jackson-databind:2.14.2' + implementation 'jakarta.xml.bind:jakarta.xml.bind-api:4.0.0' + implementation 'org.apache.logging.log4j:log4j-core:2.19.0' + implementation 'org.apache.logging.log4j:log4j-api:2.19.0' + + // spring management + compileOnly libs.lombok + implementation libs.lombok + annotationProcessor libs.lombok +} + +test { + useJUnitPlatform() +} diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/PCRQuoteValidator.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/PCRQuoteValidator.java new file mode 100644 index 00000000..0aa2a4b4 --- /dev/null +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/PCRQuoteValidator.java @@ -0,0 +1,223 @@ +package hirs.attestationca.persist; + +import hirs.attestationca.persist.entity.userdefined.SupplyChainSettings; +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 SupplyChainSettings 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 SupplyChainSettings 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/AbstractEntity.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/AbstractEntity.java new file mode 100644 index 00000000..9f6b2a52 --- /dev/null +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/AbstractEntity.java @@ -0,0 +1,100 @@ +package hirs.attestationca.persist.entity; + +import jakarta.persistence.Column; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.MappedSuperclass; +import lombok.Getter; +import lombok.ToString; +import org.hibernate.annotations.ColumnDefault; +import org.hibernate.annotations.Generated; +import org.hibernate.annotations.GenerationTime; +import org.hibernate.annotations.JdbcTypeCode; + +import java.io.Serializable; +import java.util.Date; +import java.util.UUID; + +/** + * An abstract database entity. + */ +@ToString +@MappedSuperclass +public abstract class AbstractEntity implements Serializable { + + /** + * static value for the length of a status message for objects that + * can have extremely long values, potentially. + */ + protected static final int RESULT_MESSAGE_LENGTH = 1000000; + + @Id + @Column(name = "id") + @GeneratedValue(generator = "uuid2", strategy=GenerationType.AUTO) + @JdbcTypeCode(java.sql.Types.VARCHAR) + @Getter + private UUID id; + + @Column (name = "create_time") + @ColumnDefault(value = "CURRENT_TIMESTAMP") + @Generated(GenerationTime.INSERT) + private Date createTime;// = new Date(); + + /** + * Default empty constructor is required for Hibernate. It is protected to + * prevent code from calling it directly. + */ + protected AbstractEntity() { + super(); + } + + /** + * Setter for the UUID that can not be null + * and can not be overridden. + * @param id - primary able key + */ + public void setId(UUID id) { + if (id != null) { + this.id = id; + } + } + + /** + * Returns the creation time of this entity. + * + * @return creation time + */ + public Date getCreateTime() { + return (Date) createTime.clone(); + } + + /** + * Reset the creation time to the current time. + */ + public void resetCreateTime() { + createTime.setTime(new Date().getTime()); + } + + @Override + public int hashCode() { + if (id != null) { + return id.hashCode(); + } + return super.hashCode(); + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(this.getClass().equals(obj.getClass()))) { + return false; + } + return this.hashCode() == obj.hashCode(); + } +} diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/Appraiser.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/Appraiser.java new file mode 100644 index 00000000..4f66ef95 --- /dev/null +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/Appraiser.java @@ -0,0 +1,88 @@ +package hirs.attestationca.persist.entity; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import lombok.AccessLevel; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; + +/** + * The Appraiser class represents an appraiser that can appraise a Report. + * Appraisers are invoked to validate the integrity of client's platform. An + * Appraiser does this by examining a Report sent from the client's + * machine. + *

+ * Supported Report types are kept track of in three ways:

  • The type of report + * received for appraisal is getAppraiseReportType() (e.g. the DeviceInfoAppraiser + * takes in a DeviceInfoReport and the TPMAppraiser takes in an + * IntegrityReport)
  • The type requested in getReportRequest is + * getRequestReportType(). This tends to be the specific report type for that type of appraiser + * (e.g. the IMAAppraiser requests an IMAReport and the + * TPMAppraiser requests a TPMReport)
  • The set of types this + * appraiser relies on extracting from the top-level report is getRequiredReportTypes() (e.g. if the + * top-level report is IntegrityReport then the IMAAppraiser needs to + * extract both a DeviceInfoReport and a IMAReport from the + * IntegrityReport)
+ */ +@Entity +@Table(name = "Appraiser") +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@ToString +@EqualsAndHashCode(callSuper = false) +public class Appraiser { + /** + * Name set for every instance of TPMAppraiser. + */ + public static final String TPM_NAME = "TPM Appraiser"; + /** + * Name set for every instance of SupplyChainAppraiser. + */ + public static final String SC_NAME = "Supply Chain Appraiser"; + /** + * Name set for every instance of IMAAppraiser. + */ + public static final String IMA_NAME = "IMA Appraiser"; + /** + * Name set for every instance of HIRSAppraiser. + */ + public static final String HIRS_NAME = "HIRS Appraiser"; + /** + * Name set for every instance of DeviceInfoAppraiser. + */ + public static final String DI_NAME = "Device Info Appraiser"; + + @Getter + @ToString.Exclude + @EqualsAndHashCode.Exclude + @Id + @Column(name = "Appraiser_ID") + @GeneratedValue(strategy = GenerationType.AUTO) + private Long id; + + @Getter + @Setter + @Column(nullable = false, unique = true) + private String name; + + /** + * Creates a new Appraiser with the specified name. The name should be universally + * unique as this is how other components will identify Appraisers. Web portals, + * for instance, could display a list of Appraiser names to display which + * Appraisers are available. + *

+ * The name will be tested for uniqueness when it is added to a repository. It is not tested for + * uniqueness in the class. + * + * @param name unique name + */ + public Appraiser(final String name) { + this.name = name; + } +} diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/ArchivableEntity.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/ArchivableEntity.java new file mode 100644 index 00000000..b063fcaa --- /dev/null +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/ArchivableEntity.java @@ -0,0 +1,95 @@ +package hirs.attestationca.persist.entity; + +import jakarta.persistence.Column; +import jakarta.persistence.MappedSuperclass; +import lombok.Getter; +import lombok.ToString; + +import java.util.Date; + +/** + * An abstract archivable entity that can be deleted. + */ +@ToString +@Getter +@MappedSuperclass +public abstract class ArchivableEntity extends AbstractEntity { + + /** + * Defining the size of a message field for error display. + */ + public static final int MAX_MESSAGE_LENGTH = 2400; + + @Column(name = "archived_time") + private Date archivedTime; + + @Column(name = "archived_description") + private String archivedDescription; + + /** + * Default empty constructor is required for Hibernate. It is protected to + * prevent code from calling it directly. + */ + protected ArchivableEntity() { + super(); + } + + /** + * Return the boolean representing whether or not this entity has been soft-deleted. + * + * @return true if this entity has been soft-deleted, false otherwise + */ + public final boolean isArchived() { + return archivedTime != null; + } + + /** + * Signals that this entity has been archived, by setting the archivedTime to the current date + * and time. + * + * @return + * true if time was null and date was set. + * false is archived time is already set, signifying the entity has been archived. + */ + public final boolean archive() { + if (this.archivedTime == null) { + this.archivedTime = new Date(); + return true; + } + return false; + } + + /** + * Sets a description for the resolution if one is provided. This is done for accounting + * purposes so the reason for action taken can be referenced. + * + * @param description - description of the action taken for resolution + * @return + * boolean result is dependent on the return value of the archive() method + */ + public final boolean archive(final String description) { + if (archive()) { + this.archivedDescription = description; + return true; + } else { + return false; + } + } + + /** + * Sets the archivedTime to null. The archivedTime being null signifies that the entity has + * not been archived. If the time is already null then this call was unnecessary. + * + * @return + * true if the time is changed to null. + * false if time was already set to null. + */ + public final boolean restore() { + if (this.archivedTime != null) { + this.archivedTime = null; + this.archivedDescription = null; + return true; + } + return false; + } +} diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/Policy.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/Policy.java new file mode 100644 index 00000000..d4b1cb24 --- /dev/null +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/Policy.java @@ -0,0 +1,72 @@ +package hirs.attestationca.persist.entity; + +import jakarta.persistence.Access; +import jakarta.persistence.AccessType; +import jakarta.persistence.Inheritance; +import jakarta.persistence.InheritanceType; +import jakarta.persistence.MappedSuperclass; + +/** + * The Policy class represents a policy. This is an abstract class + * for representing the rules for which an Appraiser should + * evaluate a Report. A typical Policy will contain a + * Baseline at the very least. A Policy is identified + * by its name, so the name for a Policy must be unique. + */ +@Inheritance(strategy = InheritanceType.JOINED) +@Access(AccessType.FIELD) +@MappedSuperclass +public abstract class Policy extends UserDefinedEntity { + + /** + * Default empty constructor is required for Hibernate. It is protected to + * prevent code from calling it directly. + */ + protected Policy() { + super(); + } + + /** + * Creates a new Policy with the specified name. + * + * @param name + * name + */ + public Policy(final String name) { + super(name); + } + + /** + * Creates a new Policy with the specified name and + * description. + * + * @param name + * name (required) + * @param description + * description (may be null) + */ + public Policy(final String name, final String description) { + super(name, description); + } + + /** + * Returns true if this object has been persisted. Used in determining whether + * an Appraiser should request the full Policy (and baselines) for appraisal + * + * @return true if this object has been persisted; false otherwise + */ + public final boolean isPersisted() { + return getId() != null; + } + + /** + * When {@link Policy} are serialized to be sent to the browser, this can be used + * to determine the type of {@link Policy}. + * + * @return The class name for the {@link Policy} + */ + public String getType() { + return this.getClass().getSimpleName(); + } +} + diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/UserDefinedEntity.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/UserDefinedEntity.java new file mode 100644 index 00000000..651d3252 --- /dev/null +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/UserDefinedEntity.java @@ -0,0 +1,46 @@ +package hirs.attestationca.persist.entity; + +import jakarta.persistence.Column; +import jakarta.persistence.MappedSuperclass; +import lombok.AllArgsConstructor; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +/** + * An abstract archivable entity that can be given a user-defined name and description. + */ +@Getter +@Setter +@EqualsAndHashCode(callSuper = false) +@AllArgsConstructor +@MappedSuperclass +public abstract class UserDefinedEntity extends ArchivableEntity { + + @Column(nullable = false, unique = true) + private String name; + + @ToString.Exclude + @EqualsAndHashCode.Exclude + @Column(nullable = false, unique = false) + private String description = ""; + + /** + * Default empty constructor is required for Hibernate. It is protected to + * prevent code from calling it directly. + */ + protected UserDefinedEntity() { + super(); + } + + /** + * Creates a new entity with the specified name. + * + * @param name name + */ + public UserDefinedEntity(final String name) { + this(name, ""); + } +} + diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/manager/CertificateRepository.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/manager/CertificateRepository.java new file mode 100644 index 00000000..68c76323 --- /dev/null +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/manager/CertificateRepository.java @@ -0,0 +1,11 @@ +package hirs.attestationca.persist.entity.manager; + +import hirs.attestationca.persist.entity.userdefined.Certificate; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.UUID; + +@Repository +public interface CertificateRepository extends JpaRepository { +} diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/manager/DeviceRepository.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/manager/DeviceRepository.java new file mode 100644 index 00000000..a22d74ee --- /dev/null +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/manager/DeviceRepository.java @@ -0,0 +1,13 @@ +package hirs.attestationca.persist.entity.manager; + +import hirs.attestationca.persist.entity.userdefined.Device; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.UUID; + +@Repository +public interface DeviceRepository extends JpaRepository { + List findByName(String deviceName); +} 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 new file mode 100644 index 00000000..f6406cec --- /dev/null +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/manager/ReferenceManifestRepository.java @@ -0,0 +1,11 @@ +package hirs.attestationca.persist.entity.manager; + +import hirs.attestationca.persist.entity.userdefined.ReferenceManifest; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.UUID; + +@Repository +public interface ReferenceManifestRepository extends JpaRepository { +} diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/manager/SettingsRepository.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/manager/SettingsRepository.java new file mode 100644 index 00000000..be84710e --- /dev/null +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/manager/SettingsRepository.java @@ -0,0 +1,12 @@ +package hirs.attestationca.persist.entity.manager; + +import hirs.attestationca.persist.entity.userdefined.SupplyChainSettings; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.UUID; + +@Repository +public interface SettingsRepository extends JpaRepository { + SupplyChainSettings findByName(String name); +} diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/package-info.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/package-info.java new file mode 100644 index 00000000..fa1fa47f --- /dev/null +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/package-info.java @@ -0,0 +1,4 @@ +/** + * This package has objects for hibernate entity. + */ +package hirs.attestationca.persist.entity; diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/Certificate.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/Certificate.java new file mode 100644 index 00000000..387165f6 --- /dev/null +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/Certificate.java @@ -0,0 +1,1076 @@ +package hirs.attestationca.persist.entity.userdefined; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.google.common.base.Preconditions; +import hirs.attestationca.persist.entity.ArchivableEntity; +import hirs.attestationca.persist.entity.userdefined.certificate.CertificateVariables; +import hirs.utils.HexUtils; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Transient; +import lombok.Getter; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.bouncycastle.asn1.ASN1BitString; +import org.bouncycastle.asn1.ASN1Encodable; +import org.bouncycastle.asn1.ASN1GeneralizedTime; +import org.bouncycastle.asn1.ASN1InputStream; +import org.bouncycastle.asn1.ASN1Integer; +import org.bouncycastle.asn1.ASN1Object; +import org.bouncycastle.asn1.ASN1Primitive; +import org.bouncycastle.asn1.ASN1Sequence; +import org.bouncycastle.asn1.DERIA5String; +import org.bouncycastle.asn1.DEROctetString; +import org.bouncycastle.asn1.DERTaggedObject; +import org.bouncycastle.asn1.DLSequence; +import org.bouncycastle.asn1.x500.X500Name; +import org.bouncycastle.asn1.x509.AccessDescription; +import org.bouncycastle.asn1.x509.AttCertIssuer; +import org.bouncycastle.asn1.x509.AttributeCertificate; +import org.bouncycastle.asn1.x509.AttributeCertificateInfo; +import org.bouncycastle.asn1.x509.AuthorityInformationAccess; +import org.bouncycastle.asn1.x509.AuthorityKeyIdentifier; +import org.bouncycastle.asn1.x509.CRLDistPoint; +import org.bouncycastle.asn1.x509.DistributionPoint; +import org.bouncycastle.asn1.x509.DistributionPointName; +import org.bouncycastle.asn1.x509.Extension; +import org.bouncycastle.asn1.x509.Extensions; +import org.bouncycastle.asn1.x509.GeneralName; +import org.bouncycastle.asn1.x509.GeneralNames; +import org.bouncycastle.asn1.x509.V2Form; +import org.bouncycastle.cert.X509AttributeCertificateHolder; +import org.bouncycastle.cert.X509CertificateHolder; +import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils; +import org.bouncycastle.util.encoders.Base64; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.math.BigInteger; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.PublicKey; +import java.security.Signature; +import java.security.SignatureException; +import java.security.cert.CertificateEncodingException; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.CertificateParsingException; +import java.security.cert.X509Certificate; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Date; +import java.util.List; +import java.util.ListIterator; +import java.util.Objects; + + +/** + * This class enables the persistence of a single X509 certificates or X509 attribute certificate. + * It stores certain attributes separately from the serialized certificate to enable querying on + * those attributes. + */ +@Entity +public abstract class Certificate extends ArchivableEntity { + + private static final Logger LOGGER = LogManager.getLogger(Certificate.class); + + /** + * Holds the different certificate types. + */ + public enum CertificateType { + /** + * Basic X509 Certificate. + */ + X509_CERTIFICATE, + /** + * Basic Attribute Certificate. + */ + ATTRIBUTE_CERTIFICATE, + /** + * Invalid Certificate. + */ + INVALID_CERTIFICATE + } + + /** + * Decimal digit representation of base 16. + */ + public static final int HEX_BASE = 16; + + /** + * Min length representing the attribute certificate. + */ + public static final int MIN_ATTR_CERT_LENGTH = 8; + /** + * Holds the name of the entity 'ID' field. + */ + public static final String ID_FIELD = "id"; + + /** + * Holds the name of the entity 'Archived' field. + */ + public static final String ARCHIVE_FIELD = "archivedTime"; + + /** + * Holds the name of the 'serialNumber' field. + */ + public static final String SERIAL_NUMBER_FIELD = "serialNumber"; + @Getter + @Column(nullable = false, precision = CertificateVariables.MAX_NUMERIC_PRECISION, scale = 0) + private final BigInteger serialNumber; + + /** + * Holds the name of the 'issuer' field. + */ + public static final String ISSUER_FIELD = "issuer"; + @Column(nullable = false) + private final String issuer; + /** + * Holds the name of the 'issuerSorted' field. + */ + public static final String ISSUER_SORTED_FIELD = "issuerSorted"; + @Getter + @Column + private final String issuerSorted; + + /** + * Holds the name of the 'subject' field. + */ + public static final String SUBJECT_FIELD = "subject"; + @Getter + @Column(nullable = true) + private final String subject; + /** + * Holds the name of the 'subjectSorted' field. + */ + public static final String SUBJECT_SORTED_FIELD = "subjectSorted"; + @Getter + @Column + private final String subjectSorted; + + /** + * Holds the name of the 'encodedPublicKey' field. + */ + public static final String ENCODED_PUBLIC_KEY_FIELD = "encodedPublicKey"; + @Column(length = CertificateVariables.MAX_CERT_LENGTH_BYTES, nullable = true) + private final byte[] encodedPublicKey; + + /** + * Holds the name of the 'encodedPublicKey' field. + */ + public static final String PUBLIC_KEY_MODULUS_FIELD = "publicKeyModulusHexValue"; + + // We're currently seeing 2048-bit keys, which is 512 hex digits. + // Using a max length of 1024 for future-proofing. + @Getter + @Column(length = CertificateVariables.MAX_PUB_KEY_MODULUS_HEX_LENGTH, nullable = true) + private final String publicKeyModulusHexValue; + + @Column(length = CertificateVariables.MAX_CERT_LENGTH_BYTES, nullable = false) + private final byte[] signature; + + @Column(nullable = false) + private final Date beginValidity; + + @Column(nullable = false) + private final Date endValidity; + + @Column(length = CertificateVariables.MAX_CERT_LENGTH_BYTES, nullable = false) + @JsonIgnore + private byte[] certificateBytes; + + /** + * Holds the name of the 'certificateHash' field. + */ + public static final String CERTIFICATE_HASH_FIELD = "certificateHash"; + @Column(nullable = false) + @JsonIgnore @Getter + private final int certificateHash; + + /** + * This field exists to enforce a unique constraint on a hash over the certificate contents + * and the certificate type. This is to ensure the system only allows one copy of a + * certificate per role in the system. + */ + @Column(nullable = false, unique = true) + @JsonIgnore + private final int certAndTypeHash; + + /** + * Holds the name of the 'holderSerialNumber' field. + */ + public static final String HOLDER_SERIAL_NUMBER_FIELD = "holderSerialNumber"; + + @Getter + @Column(nullable = false, precision = CertificateVariables.MAX_NUMERIC_PRECISION, scale = 0) + private final BigInteger holderSerialNumber; + @Getter + private String holderIssuer; + @Getter + @Column(nullable = true, precision = CertificateVariables.MAX_NUMERIC_PRECISION, scale = 0) + private final BigInteger authoritySerialNumber; + + @SuppressWarnings("PMD.AvoidUsingHardCodedIP") // this is not an IP address; PMD thinks it is + private static final String POLICY_CONSTRAINTS = "2.5.29.36"; + + // we don't need to persist this, but we don't want to unpack this cert multiple times + @Transient + private X509Certificate parsedX509Cert = null; + + @Getter + private String signatureAlgorithm, publicKeyAlgorithm; + @Getter + private String keyUsage, extendedKeyUsage; + private byte[] policyConstraints; + /** + * Holds the name of the 'authorityKeyIdentifier' field. + */ + public static final String AUTHORITY_KEY_ID_FIELD = "authorityKeyIdentifier"; + @Getter + private String authorityKeyIdentifier; + @Getter + private String authorityInfoAccess; + @Getter + private String crlPoints; + @Getter + private int publicKeySize; + + /** + * Default constructor necessary for Hibernate. + */ + protected Certificate() { + super(); + this.serialNumber = BigInteger.ZERO; + this.issuer = null; + this.subject = null; + this.issuerSorted = null; + this.subjectSorted = null; + + this.encodedPublicKey = null; + this.publicKeyModulusHexValue = null; + this.signature = null; + this.beginValidity = null; + this.endValidity = null; + this.certificateBytes = null; + this.certificateHash = 0; + this.certAndTypeHash = 0; + this.holderSerialNumber = BigInteger.ZERO; + this.holderIssuer = null; + this.publicKeyAlgorithm = null; + this.signatureAlgorithm = null; + this.keyUsage = null; + this.extendedKeyUsage = null; + this.policyConstraints = null; + this.authorityKeyIdentifier = null; + this.authorityInfoAccess = null; + this.authoritySerialNumber = BigInteger.ZERO; + this.crlPoints = null; + this.publicKeySize = 0; + } + + /** + * Construct a new Certificate by parsing the file at the given path. The given certificate + * should represent either an X509 certificate or X509 attribute certificate. + * + * @param certificatePath the path on disk to a certificate + * @throws IOException if there is a problem reading the file + */ + public Certificate(final Path certificatePath) throws IOException { + this(readBytes(certificatePath)); + } + + /** + * Construct a new Certificate given its binary contents. The given certificate should + * represent either an X509 certificate or X509 attribute certificate. + * + * @param certificateBytes the contents of a certificate file + * @throws IOException if there is a problem extracting information from the certificate + */ + @SuppressWarnings("methodlength") + public Certificate(final byte[] certificateBytes) throws IOException { + Preconditions.checkArgument( + certificateBytes != null, + "Cannot construct a Certificate from a null byte array" + ); + + Preconditions.checkArgument( + certificateBytes.length > 0, + "Cannot construct a Certificate from an empty byte array" + ); + + this.certificateBytes = certificateBytes.clone(); + + // check for and handle possible PEM base 64 encoding + String possiblePem = new String(certificateBytes, StandardCharsets.UTF_8); + if (isPEM(possiblePem)) { + possiblePem = possiblePem.replace(CertificateVariables.PEM_HEADER, ""); + possiblePem = possiblePem.replace(CertificateVariables.PEM_FOOTER, ""); + possiblePem = possiblePem.replace(CertificateVariables.PEM_ATTRIBUTE_HEADER, ""); + possiblePem = possiblePem.replace(CertificateVariables.PEM_ATTRIBUTE_FOOTER, ""); + this.certificateBytes = Base64.decode(possiblePem); + } + + AuthorityKeyIdentifier authKeyIdentifier; + this.certificateBytes = trimCertificate(this.certificateBytes); + + // Extract certificate data + switch (getCertificateType()) { + case X509_CERTIFICATE: + X509Certificate x509Certificate = getX509Certificate(); + this.serialNumber = x509Certificate.getSerialNumber(); + this.issuer = x509Certificate.getIssuerX500Principal().getName(); + this.subject = x509Certificate.getSubjectX500Principal().getName(); + this.encodedPublicKey = x509Certificate.getPublicKey().getEncoded(); + BigInteger publicKeyModulus = getPublicKeyModulus(x509Certificate); + + if (publicKeyModulus != null) { + this.publicKeyModulusHexValue = publicKeyModulus.toString(HEX_BASE); + this.publicKeySize = publicKeyModulus.bitLength(); + } else { + this.publicKeyModulusHexValue = null; + } + this.publicKeyAlgorithm = x509Certificate.getPublicKey().getAlgorithm(); + this.signatureAlgorithm = x509Certificate.getSigAlgName(); + this.signature = x509Certificate.getSignature(); + this.beginValidity = x509Certificate.getNotBefore(); + this.endValidity = x509Certificate.getNotAfter(); + this.holderSerialNumber = BigInteger.ZERO; + this.issuerSorted = parseSortDNs(this.issuer); + this.subjectSorted = parseSortDNs(this.subject); + this.policyConstraints = x509Certificate + .getExtensionValue(POLICY_CONSTRAINTS); + authKeyIdentifier = AuthorityKeyIdentifier + .getInstance((DLSequence) getExtensionValue( + Extension.authorityKeyIdentifier.getId())); + + this.authorityInfoAccess = getAuthorityInfoAccess(x509Certificate + .getExtensionValue(Extension.authorityInfoAccess.getId())); + this.keyUsage = parseKeyUsage(x509Certificate.getKeyUsage()); + this.crlPoints = getCRLDistributionPoint(); + + try { + if (x509Certificate.getExtendedKeyUsage() != null) { + StringBuilder sb = new StringBuilder(); + for (String s : x509Certificate.getExtendedKeyUsage()) { + sb.append(String.format("%s%n", s)); + } + this.extendedKeyUsage = sb.toString(); + } + } catch (CertificateParsingException ex) { + // do nothing + } + break; + + case ATTRIBUTE_CERTIFICATE: + AttributeCertificate attCert = getAttributeCertificate(); + AttributeCertificateInfo attCertInfo = attCert.getAcinfo(); + if (attCertInfo == null) { + throw new IllegalArgumentException("Required attribute certificate info" + + " field not found in provided attribute certificate."); + } + + // Set null values (Attribute certificates do not have this values) + this.subject = null; + this.subjectSorted = null; + this.encodedPublicKey = null; + this.publicKeyModulusHexValue = null; + this.publicKeySize = 0; + + authKeyIdentifier = null; + Extensions attCertInfoExtensions = attCertInfo.getExtensions(); + if (attCertInfoExtensions != null) { + authKeyIdentifier = AuthorityKeyIdentifier + .fromExtensions(attCertInfoExtensions); + this.authorityInfoAccess = getAuthorityInfoAccess( + AuthorityInformationAccess.fromExtensions( + attCertInfoExtensions)); + } + + switch (attCert.getSignatureAlgorithm().getAlgorithm().getId()) { + case CertificateVariables.RSA256_OID: + this.signatureAlgorithm = CertificateVariables.RSA256_STRING; + break; + case CertificateVariables.RSA384_OID: + this.signatureAlgorithm = CertificateVariables.RSA384_STRING; + break; + case CertificateVariables.RSA224_OID: + this.signatureAlgorithm = CertificateVariables.RSA224_STRING; + break; + case CertificateVariables.RSA512_OID: + this.signatureAlgorithm = CertificateVariables.RSA512_STRING; + break; + case CertificateVariables.RSA512_224_OID: + this.signatureAlgorithm = CertificateVariables.RSA512_224_STRING; + break; + case CertificateVariables.RSA512_256_OID: + this.signatureAlgorithm = CertificateVariables.RSA512_256_STRING; + break; + case CertificateVariables.ECDSA_OID: + this.signatureAlgorithm = CertificateVariables.ECDSA_STRING; + break; + case CertificateVariables.ECDSA_SHA224_OID: + this.signatureAlgorithm = CertificateVariables.ECDSA_SHA224_STRING; + break; + default: + break; + } + + // Get attribute certificate information + this.serialNumber = attCertInfo.getSerialNumber().getValue(); + this.holderSerialNumber = attCertInfo + .getHolder() + .getBaseCertificateID() + .getSerial() + .getValue(); + this.holderIssuer = attCertInfo.getHolder() + .getBaseCertificateID().getIssuer() + .getNames()[0].getName().toString(); + this.signature = attCert.getSignatureValue().getBytes(); + this.issuer = getAttributeCertificateIssuerNames( + attCertInfo.getIssuer())[0].toString(); + this.issuerSorted = parseSortDNs(this.issuer); + + // Parse notBefore and notAfter dates + this.beginValidity = recoverDate(attCertInfo + .getAttrCertValidityPeriod() + .getNotBeforeTime()); + this.endValidity = recoverDate(attCertInfo + .getAttrCertValidityPeriod() + .getNotAfterTime()); + break; + default: + throw new IllegalArgumentException("Cannot recognize certificate type."); + } + + BigInteger authSerialNumber = null; + if (authKeyIdentifier != null) { + this.authorityKeyIdentifier = authKeyIdentifierToString(authKeyIdentifier); + authSerialNumber = authKeyIdentifier.getAuthorityCertSerialNumber(); + } + + if (authSerialNumber != null) { + this.authoritySerialNumber = authSerialNumber; + } else { + this.authoritySerialNumber = BigInteger.ZERO; + } + + this.certificateHash = Arrays.hashCode(this.certificateBytes); + this.certAndTypeHash = Objects.hash(certificateHash, getClass().getSimpleName()); + } + + @SuppressWarnings("magicnumber") + private byte[] trimCertificate(final byte[] certificateBytes) { + int certificateStart = 0; + int certificateLength = 0; + ByteBuffer certificateByteBuffer = ByteBuffer.wrap(certificateBytes); + + StringBuilder malformedCertStringBuilder = new StringBuilder(CertificateVariables.MALFORMED_CERT_MESSAGE); + while (certificateByteBuffer.hasRemaining()) { + // Check if there isn't an ASN.1 structure in the provided bytes + if (certificateByteBuffer.remaining() <= 2) { + throw new IllegalArgumentException(malformedCertStringBuilder + .append(" No certificate length field could be found.").toString()); + } + + // Look for first ASN.1 Sequence marked by the two bytes (0x30) and (0x82) + // The check advances our position in the ByteBuffer by one byte + int currentPosition = certificateByteBuffer.position(); + if (certificateByteBuffer.get() == (byte) 0x30 + && certificateByteBuffer.get(currentPosition + 1) == (byte) 0x82) { + // Check if we have anything more in the buffer than an ASN.1 Sequence header + if (certificateByteBuffer.remaining() <= 3) { + throw new IllegalArgumentException(malformedCertStringBuilder + .append(" Certificate is nothing more than ASN.1 Sequence.") + .toString()); + } + // Mark the start of the first ASN.1 Sequence / Certificate Body + certificateStart = currentPosition; + + // Parse the length as the 2-bytes following the start of the ASN.1 Sequence + certificateLength = Short.toUnsignedInt( + certificateByteBuffer.getShort(currentPosition + 2)); + // Add the 4 bytes that comprise the start of the ASN.1 Sequence and the length + certificateLength += 4; + break; + } + } + + if (certificateStart + certificateLength > certificateBytes.length) { + throw new IllegalArgumentException(malformedCertStringBuilder + .append(" Value of certificate length field extends beyond length") + .append(" of provided certificate.").toString()); + } + // Return bytes representing the main certificate body + return Arrays.copyOfRange(certificateBytes, certificateStart, + certificateStart + certificateLength); + } + + /** + * Getter for the CRL Distribution that is reference by the Revocation Locator + * on the portal. + * + * @return A list of URLs that inform the location of the certificate revocation lists + * @throws java.io.IOException + */ + private String getCRLDistributionPoint() throws IOException { + List crlUrls = new ArrayList<>(); + ASN1Primitive primitive = getExtensionValue(Extension.cRLDistributionPoints.getId()); + StringBuilder sb = new StringBuilder(); + + if (primitive != null) { + CRLDistPoint crlDistPoint = CRLDistPoint.getInstance(primitive); + DistributionPoint[] distributionPoints = crlDistPoint.getDistributionPoints(); + + for (DistributionPoint distributionPoint : distributionPoints) { + DistributionPointName dpn = distributionPoint.getDistributionPoint(); + // Look for URIs in fullName + if (dpn != null && dpn.getType() == DistributionPointName.FULL_NAME) { + GeneralName[] genNames = GeneralNames.getInstance(dpn.getName()) + .getNames(); + for (GeneralName genName : genNames) { + if (genName.getTagNo() == GeneralName.uniformResourceIdentifier) { + String url = DERIA5String.getInstance(genName.getName()) + .getString(); + crlUrls.add(url); + } + } + } + } + } + + for (String s : crlUrls) { + sb.append(String.format("%s%n", s)); + } + + return sb.toString(); + } + + /** + * Getter for the x509 Platform Certificate version. + * @return a big integer representing the certificate version. If there + * is an error, return the max value to visible show error. + */ + public int getX509CredentialVersion() { + try { + return getX509Certificate().getVersion() - 1; + } catch (IOException ex) { + LOGGER.warn("X509 Credential Version not found."); + LOGGER.error(ex); + return Integer.MAX_VALUE; + } + } + + /** + * Checks if another certificate is the issuer for this certificate. + * + * @param issuer the other certificate to check (must be an X509Certificate, + * not an X509AttributeCertificateHolder) + * @return whether or not the other certificate is the issuer for this certificate + * @throws IOException if there is an issue deserializing either certificate + */ + public String isIssuer(final Certificate issuer) throws IOException { + String isIssuer = "Certificate signature failed to verify"; + // only run if of the correct type, otherwise false + if (issuer.getCertificateType() == CertificateType.X509_CERTIFICATE) { + X509Certificate issuerX509 = issuer.getX509Certificate(); + // Validate if it's the issuer + switch (getCertificateType()) { + case X509_CERTIFICATE: + X509Certificate certX509 = getX509Certificate(); + try { + certX509.verify(issuerX509.getPublicKey()); + isIssuer = ""; + } catch (CertificateException | NoSuchAlgorithmException | InvalidKeyException + | NoSuchProviderException | SignatureException e) { + LOGGER.error(e); + } + break; + case ATTRIBUTE_CERTIFICATE: + AttributeCertificate attCert = getAttributeCertificate(); + try { + Signature sig = Signature.getInstance(this.getSignatureAlgorithm()); + sig.initVerify(issuerX509.getPublicKey()); + sig.update(attCert.getAcinfo().getEncoded()); + if (sig.verify(attCert.getSignatureValue().getBytes())) { + isIssuer = ""; + } + } catch (NoSuchAlgorithmException + | InvalidKeyException + | SignatureException sigEx) { + LOGGER.error(sigEx); + } + break; + default: + break; + } + } + + return isIssuer; + } + + /** + * Return whether or not this certificate is valid on a particular date. + * + * @param date the date of interest. + * @return true if the attribute certificate is valid, false otherwise. + */ + public boolean isValidOn(final Date date) { + return !date.before(getBeginValidity()) && !date.after(getEndValidity()); + } + + /** + * Retrieve the original X509 certificate. + * + * @return the original X509 certificate + * @throws IOException if there is a problem deserializing the certificate as an X509 cert + */ + @JsonIgnore + public X509Certificate getX509Certificate() throws IOException { + if (parsedX509Cert != null) { + return parsedX509Cert; + } + + try (ByteArrayInputStream certInputStream = new ByteArrayInputStream(certificateBytes)) { + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + parsedX509Cert = (X509Certificate) cf.generateCertificate(certInputStream); + return parsedX509Cert; + } catch (CertificateException e) { + throw new IOException("Cannot construct X509Certificate from the input stream", e); + } + } + + /** + * @return the type of certificate. + * @throws java.io.IOException if there is a problem extracting information from the certificate + */ + protected CertificateType getCertificateType() throws IOException { + //Parse the certificate into a sequence + ASN1Sequence testCred1 = (ASN1Sequence) ASN1Primitive.fromByteArray(this.certificateBytes); + ASN1Sequence testSeq = (ASN1Sequence) ((ASN1Object) testCred1.toArray()[0]); + + if (testSeq.toArray()[0] instanceof ASN1Integer) { + if (testSeq.toArray().length >= MIN_ATTR_CERT_LENGTH) { + // Attribute Certificate + return CertificateType.ATTRIBUTE_CERTIFICATE; + } else { + // V1 X509Certificate + return CertificateType.X509_CERTIFICATE; + } + } else if (testSeq.toArray()[0] instanceof DERTaggedObject) { + // V2 or V3 X509Certificate + return CertificateType.X509_CERTIFICATE; + } + + return CertificateType.INVALID_CERTIFICATE; + } + + private boolean isPEM(final String possiblePEM) { + return possiblePEM.contains(CertificateVariables.PEM_HEADER) + || possiblePEM.contains(CertificateVariables.PEM_ATTRIBUTE_HEADER); + } + + private String parseKeyUsage(final boolean[] bools) { + StringBuilder sb = new StringBuilder(); + + if (bools != null) { + for (int i = 0; i < bools.length; i++) { + if (bools[i]) { + sb.append(getKeyUsageString(i)); + } + } + } + + return sb.toString(); + } + + /** + * Return the string associated with the boolean slot. + * @param bit associated with the location in the array. + * @return string value of the bit set. + */ + private String getKeyUsageString(final int bit) { + String tempStr = ""; + + switch (bit) { + case CertificateVariables.KEY_USAGE_BIT0: + tempStr = String.format("%s%n", CertificateVariables.KEY_USAGE_DS); + break; + case CertificateVariables.KEY_USAGE_BIT1: + tempStr = String.format("%s%n", CertificateVariables.KEY_USAGE_NR); + break; + case CertificateVariables.KEY_USAGE_BIT2: + tempStr = String.format("%s%n", CertificateVariables.KEY_USAGE_KE); + break; + case CertificateVariables.KEY_USAGE_BIT3: + tempStr = String.format("%s%n", CertificateVariables.KEY_USAGE_DE); + break; + case CertificateVariables.KEY_USAGE_BIT4: + tempStr = String.format("%s%n", CertificateVariables.KEY_USAGE_KA); + break; + case CertificateVariables.KEY_USAGE_BIT5: + tempStr = String.format("%s%n", CertificateVariables.KEY_USAGE_KC); + break; + case CertificateVariables.KEY_USAGE_BIT6: + tempStr = String.format("%s%n", CertificateVariables.KEY_USAGE_CS); + break; + case CertificateVariables.KEY_USAGE_BIT7: + tempStr = String.format("%s%n", CertificateVariables.KEY_USAGE_EO); + break; + case CertificateVariables.KEY_USAGE_BIT8: + tempStr = String.format("%s%n", CertificateVariables.KEY_USAGE_DO); + break; + default: + break; + } + + return tempStr; + } + + /** + * Getter for the authorityKeyIdentifier. + * @return the ID's byte representation + */ + private String authKeyIdentifierToString(final AuthorityKeyIdentifier aki) { + String retValue = ""; + if (aki != null) { + byte[] keyArray = aki.getKeyIdentifier(); + if (keyArray != null) { + retValue = HexUtils.byteArrayToHexString(keyArray); + } + } + + return retValue; + } + + /** + * Gets the contents of requested OID. + * + * @param oid Object Identifier + * @return ASN1Primitive Content related to the requested OID + * @throws java.io.IOException + */ + private ASN1Primitive getExtensionValue(final String oid) throws IOException { + byte[] extensionValue = getX509Certificate().getExtensionValue(oid); + ASN1Primitive asn1Primitive = null; + ASN1InputStream asn1InputStream = null; + + if (extensionValue != null) { + try { + asn1InputStream = new ASN1InputStream(extensionValue); + DEROctetString oct = (DEROctetString) asn1InputStream.readObject(); + asn1InputStream.close(); + asn1InputStream = new ASN1InputStream(oct.getOctets()); + asn1Primitive = asn1InputStream.readObject(); + } catch (IOException ioEx) { + LOGGER.error(ioEx); + } finally { + if (asn1InputStream != null) { + asn1InputStream.close(); + } + } + } + + return asn1Primitive; + } + + /** + * Getter for the AuthorityInfoAccess extension value on list format. + * + * @return List Authority info access list + */ + private String getAuthorityInfoAccess(final byte[] authInfoAccess) { + StringBuilder sb = new StringBuilder(); + + try { + if (authInfoAccess != null && authInfoAccess.length > 0) { + sb.append(getAuthorityInfoAccess(AuthorityInformationAccess + .getInstance(JcaX509ExtensionUtils.parseExtensionValue(authInfoAccess)))); + } + } catch (IOException ioEx) { + LOGGER.error(ioEx); + } + + return sb.toString(); + } + + /** + * Getter for the AuthorityInfoAccess extension value on list format. + * + * @return List Authority info access list + */ + private String getAuthorityInfoAccess(final AuthorityInformationAccess authInfoAccess) { + StringBuilder sb = new StringBuilder(); + + if (authInfoAccess != null) { + for (AccessDescription desc : authInfoAccess.getAccessDescriptions()) { + if (desc.getAccessLocation().getTagNo() == GeneralName + .uniformResourceIdentifier) { + sb.append(String.format("%s%n", ((DERIA5String) desc + .getAccessLocation() + .getName()) + .getString())); + } + } + } + + return sb.toString(); + } + + /** + * This method is to take the DNs from certificates and sort them in an order + * that will be used to lookup issuer certificates. This will not be stored in + * the certificate, just the DB for lookup. + * @param distinguishedName the original DN string. + * @return a modified string of sorted DNs + */ + public static String parseSortDNs(final String distinguishedName) { + StringBuilder sb = new StringBuilder(); + String dnsString; + + if (distinguishedName == null || distinguishedName.isEmpty()) { + sb.append("BLANK"); + } else { + dnsString = distinguishedName.trim(); + dnsString = dnsString.toLowerCase(); + List dnValArray = Arrays.asList(dnsString.split(",")); + Collections.sort(dnValArray); + ListIterator dnListIter = dnValArray.listIterator(); + while (dnListIter.hasNext()) { + sb.append(dnListIter.next()); + if (dnListIter.hasNext()) { + sb.append(","); + } + } + } + + return sb.toString(); + } + + /** + * Retrieve the original X509 attribute certificate. + * + * @return the original X509 attribute certificate + * @throws IOException if there is a problem deserializing the certificate as an X509 + * attribute cert + */ + @JsonIgnore + public X509AttributeCertificateHolder getX509AttributeCertificateHolder() throws IOException { + return new X509AttributeCertificateHolder(certificateBytes); + } + + /** + * Retrieve the original Attribute Certificate. + * + * @return the original Attribute Certificate + * @throws IOException if there is a problem deserializing the certificate as an X509 + * attribute cert + */ + @JsonIgnore + public AttributeCertificate getAttributeCertificate() throws IOException { + return AttributeCertificate + .getInstance(ASN1Primitive.fromByteArray(certificateBytes)); + } + + /** + * @return this certificate's validity start date + */ + public Date getBeginValidity() { + return new Date(beginValidity.getTime()); + } + + /** + * @return this certificate's validity end date + */ + public Date getEndValidity() { + return new Date(endValidity.getTime()); + } + + /** + * Getter for the policy statement. + * @return cloned bit representation of constraints + */ + public byte[] getPolicyConstraints() { + if (policyConstraints != null) { + return policyConstraints.clone(); + } + return null; + } + + /** + * @return this certificate's encoded public key + */ + public byte[] getEncodedPublicKey() { + if (encodedPublicKey == null) { + return null; + } else { + return encodedPublicKey.clone(); + } + } + + /** + * Gets the raw bytes for the certificate. + * + * @return copy of the certificate bytes + */ + @JsonIgnore + public byte[] getRawBytes() { + if (this.certificateBytes != null) { + return this.certificateBytes.clone(); + } + return null; + } + + @Override + public String toString() { + return String.format("Certificate{%s, AuthID=%s, serialNumber=%s, " + + "issuer=%s, AuthSerialNumber=%s, publicKeySize=%d, " + + "signatureAlg=%s, Hash=%d}", super.toString(), + authorityKeyIdentifier, serialNumber.toString(), + issuer, authoritySerialNumber.toString(), publicKeySize, + signatureAlgorithm, certificateHash); + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + + if (o == null || getClass() != o.getClass()) { + return false; + } + + Certificate that = (Certificate) o; + + return Arrays.equals(certificateBytes, that.certificateBytes); + } + + @Override + public int hashCode() { + return Arrays.hashCode(certificateBytes); + } + + /** + * + * Gets the raw bytes for the certificate. + * @param certificatePath path to the certificate file + * @return bytes from the certificate file + * @throws IOException if there is a problem reading the file + */ + public static byte[] readBytes(final Path certificatePath) throws IOException { + Preconditions.checkArgument( + certificatePath != null, + "Cannot construct a Certificate from a null path" + ); + + return Files.readAllBytes(certificatePath); + } + + /** + * Retrieve an RSA-based X509 certificate's public key modulus. + * + * @param certificate the certificate holding a public key + * @return a BigInteger representing its public key's modulus or null if none found + * @throws IOException if there is an issue decoding the encoded public key + */ + public static BigInteger getPublicKeyModulus(final X509Certificate certificate) + throws IOException { + X509CertificateHolder certificateHolder = null; + try { + certificateHolder = new X509CertificateHolder(certificate.getEncoded()); + } catch (CertificateEncodingException e) { + throw new IOException("Could not encode certificate", e); + } + try { + return getPublicKeyModulus( + certificateHolder.getSubjectPublicKeyInfo().parsePublicKey().toASN1Primitive() + ); + } catch (IOException e) { + LOGGER.info("No RSA Key Detected in certificate"); + return null; + } + } + + /** + * Retrieves the modulus of the given PublicKey. + * + * @param publicKey the public key + * @return a BigInteger representing the public key's modulus + * @throws IOException if there is an issue decoding the public key + */ + public static BigInteger getPublicKeyModulus(final PublicKey publicKey) throws IOException { + ASN1Primitive publicKeyASN1 = ASN1Primitive.fromByteArray(publicKey.getEncoded()); + if (publicKeyASN1 instanceof ASN1Sequence) { + ASN1Sequence publicKeyASN1Sequence = (ASN1Sequence) publicKeyASN1; + ASN1BitString encodedModulusAndExponent = (ASN1BitString) + publicKeyASN1Sequence.getObjectAt(1); + byte[] modulusAndExponentBytes = encodedModulusAndExponent.getOctets(); + return getPublicKeyModulus(ASN1Primitive.fromByteArray(modulusAndExponentBytes)); + } else { + throw new IOException("Could not read public key as ASN1Sequence"); + } + } + + private static BigInteger getPublicKeyModulus(final ASN1Primitive publicKey) + throws IOException { + if (publicKey instanceof ASN1Sequence) { + ASN1Sequence pubKeySeq = (ASN1Sequence) publicKey; + ASN1Encodable modulus = pubKeySeq.getObjectAt(0); + if (modulus instanceof ASN1Integer) { + return ((ASN1Integer) modulus).getValue(); + } else { + throw new IOException("Could not read modulus as an ASN1Integer"); + } + } else { + throw new IOException("Could not parse public key information as an ASN1Sequence"); + } + } + + /** + * Retrieve the X509 Name array from the issuer in an Attribute Certificate. + * + * @param issuer for the Attribute Certificate + * @return a X500Name[] representing the names of the issuer + */ + public static X500Name[] getAttributeCertificateIssuerNames(final AttCertIssuer issuer) { + final ASN1Encodable form = issuer.getIssuer(); + GeneralNames name; + if (form instanceof V2Form) { + name = ((V2Form) form).getIssuerName(); + } else { + name = (GeneralNames) form; + } + + GeneralName[] names = name.getNames(); + List l = new ArrayList<>(names.length); + + for (int i = 0; i != names.length; i++) { + if (names[i].getTagNo() == GeneralName.directoryName) { + l.add(X500Name.getInstance(names[i].getName())); + } + } + + return (X500Name[]) l.toArray(new X500Name[l.size()]); + } + + /** + * Retrieve the Date from an ASN1GeneralizedTime. + * + * @param time (ASN1GeneralizedTime) of the certificate + * @return the Date from a ASN1GeneralizedTime + */ + public static Date recoverDate(final ASN1GeneralizedTime time) { + try { + return time.getDate(); + } catch (ParseException e) { + throw new IllegalStateException("unable to recover date: " + e.getMessage()); + } + } +} 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 new file mode 100644 index 00000000..738d7ac1 --- /dev/null +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/Device.java @@ -0,0 +1,63 @@ +package hirs.attestationca.persist.entity.userdefined; + +import hirs.attestationca.persist.entity.AbstractEntity; +import hirs.attestationca.persist.enums.AppraisalStatus; +import hirs.attestationca.persist.enums.HealthStatus; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.Table; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.sql.Timestamp; + +@Entity +@Table(name = "Device") +@Getter +@Setter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor +public class Device extends AbstractEntity { + + @Column(name = "name", unique = true) + private String name; + +// @OneToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER, +// optional = true, orphanRemoval = true) +// private DeviceInfoReport deviceInfo; + + @Column + @Enumerated(EnumType.ORDINAL) + private HealthStatus healthStatus; + + @Column + @Enumerated(EnumType.ORDINAL) + private AppraisalStatus.Status supplyChainValidationStatus; + + /** + * Time stamp for the report. + */ + @Column(name = "last_report_timestamp") + private Timestamp lastReportTimestamp; + + @Column(name = "is_state_overridden") + private boolean isStateOverridden; + + @Column(name = "state_override_reason") + private String overrideReason; + + @Column(name = "summary_id") + private String summaryId; + + public String toString() { + return String.format("Device Name: %s%nStatus: %s%nSummary: %s", + name, healthStatus.getStatus(), +// supplyChainValidationStatus.toString(), + summaryId); + } +} \ No newline at end of file diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/ReferenceDigestValue.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/ReferenceDigestValue.java new file mode 100644 index 00000000..4d7949c9 --- /dev/null +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/ReferenceDigestValue.java @@ -0,0 +1,72 @@ +package hirs.attestationca.persist.entity.userdefined; + +import hirs.attestationca.persist.entity.ArchivableEntity; +import jakarta.persistence.Access; +import jakarta.persistence.AccessType; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; +import org.hibernate.annotations.JdbcTypeCode; + +import java.util.UUID; + +/** + * This class represents that actual entry in the Support RIM. + * Digest Value, Event Type, index, RIM Tagid + */ +@ToString @EqualsAndHashCode(callSuper = false) +@Setter @Getter +@Entity +@Table(name = "ReferenceDigestValue") +@Access(AccessType.FIELD) +public class ReferenceDigestValue extends ArchivableEntity { + +// @Type(type = "uuid-char") + @JdbcTypeCode(java.sql.Types.VARCHAR) + @Column + private UUID baseRimId; +// @Type(type = "uuid-char") + @JdbcTypeCode(java.sql.Types.VARCHAR) + @Column + private UUID supportRimId; + @Column(nullable = false) + private String manufacturer; + @Column(nullable = false) + private String model; + @Column(nullable = false) + private int pcrIndex; + @Column(nullable = false) + private String digestValue; + @Column(nullable = false) + private String eventType; + @Column(columnDefinition = "blob", nullable = true) + private byte[] contentBlob; + @Column(nullable = false) + private boolean matchFail; + @Column(nullable = false) + private boolean patched = false; + @Column(nullable = false) + private boolean updated = false; + + /** + * Default constructor necessary for Hibernate. + */ + protected ReferenceDigestValue() { + super(); + this.baseRimId = null; + this.supportRimId = null; + this.manufacturer = ""; + this.model = ""; + this.pcrIndex = -1; + this.digestValue = ""; + this.eventType = ""; + this.matchFail = false; + this.patched = false; + this.updated = false; + this.contentBlob = null; + } +} diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/ReferenceManifest.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/ReferenceManifest.java new file mode 100644 index 00000000..0cdb64ca --- /dev/null +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/ReferenceManifest.java @@ -0,0 +1,156 @@ +package hirs.attestationca.persist.entity.userdefined; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.google.common.base.Preconditions; +import hirs.attestationca.persist.entity.ArchivableEntity; +import jakarta.persistence.Access; +import jakarta.persistence.AccessType; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; +import lombok.extern.log4j.Log4j2; +import org.apache.commons.codec.binary.Hex; +import org.hibernate.annotations.JdbcTypeCode; + +import javax.xml.XMLConstants; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.UUID; + +/** + * This class represents the Reference Integrity Manifest object that will be + * loaded into the DB and displayed in the ACA. + */ +@Getter @Setter @ToString +@EqualsAndHashCode(onlyExplicitlyIncluded = true, callSuper = false) +@Log4j2 +@Entity +@Table(name = "ReferenceManifest") +@Access(AccessType.FIELD) +public class ReferenceManifest extends ArchivableEntity { + + /** + * Holds the name of the 'hexDecHash' field. + */ + public static final String HEX_DEC_HASH_FIELD = "hexDecHash"; + /** + * String for display of a Base RIM. + */ + public static final String BASE_RIM = "Base"; + /** + * String for display of a Support RIM. + */ + public static final String SUPPORT_RIM = "Support"; + /** + * String for display of a Support RIM. + */ + public static final String MEASUREMENT_RIM = "Measurement"; + + /** + * String for the xml schema ios standard. + */ + public static final String SCHEMA_STATEMENT = "ISO/IEC 19770-2:2015 Schema (XSD 1.0) " + + "- September 2015, see http://standards.iso.org/iso/19770/-2/2015/schema.xsd"; + /** + * String for the xml schema URL file name. + */ + public static final String SCHEMA_URL = "swid_schema.xsd"; + /** + * String for the language type for the xml schema. + */ + public static final String SCHEMA_LANGUAGE = XMLConstants.W3C_XML_SCHEMA_NS_URI; + /** + * String for the package location of the xml generated java files. + */ + public static final String SCHEMA_PACKAGE = "hirs.utils.xjc"; + + @EqualsAndHashCode.Include + @Column(columnDefinition = "mediumblob", nullable = false) + private byte[] rimBytes; + @EqualsAndHashCode.Include + @Column(nullable = false) + private String rimType = "Base"; + @Column + private String tagId = null; + @Column + private boolean swidPatch = false; + @Column + private boolean swidSupplemental = false; + @Column + private String platformManufacturer = null; + @Column + private String platformManufacturerId = null; + @Column + private String swidTagVersion = null; + @Column + private String swidVersion = null; + @Column + private String platformModel = null; + @Column(nullable = false) + private String fileName = null; +// @Type(type="uuid-char") + @JdbcTypeCode(java.sql.Types.VARCHAR) + @Column + private UUID associatedRim; + @Column + private String deviceName; + @Column + private String hexDecHash = ""; + @Column + private String eventLogHash = ""; + + /** + * Default constructor necessary for Hibernate. + */ + protected ReferenceManifest() { + super(); + this.rimBytes = null; + this.rimType = null; + this.platformManufacturer = null; + this.platformManufacturerId = null; + this.platformModel = null; + this.fileName = BASE_RIM; + this.tagId = null; + this.associatedRim = null; + } + + /** + * Default constructor for ingesting the bytes of the file content. + * @param rimBytes - file contents. + */ + public ReferenceManifest(final byte[] rimBytes) { + Preconditions.checkArgument(rimBytes != null, + "Cannot construct a RIM from a null byte array"); + + Preconditions.checkArgument(rimBytes.length > 0, + "Cannot construct a RIM from an empty byte array"); + + this.rimBytes = rimBytes.clone(); + MessageDigest digest = null; + this.hexDecHash = ""; + try { + digest = MessageDigest.getInstance("SHA-256"); + this.hexDecHash = Hex.encodeHexString( + digest.digest(rimBytes)); + } catch (NoSuchAlgorithmException noSaEx) { + log.error(noSaEx); + } + } + + /** + * Getter for the Reference Integrity Manifest as a byte array. + * + * @return array of bytes + */ + @JsonIgnore + public byte[] getRimBytes() { + if (this.rimBytes != null) { + return this.rimBytes.clone(); + } + return null; + } +} diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/Report.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/Report.java new file mode 100644 index 00000000..d6c4ff2b --- /dev/null +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/Report.java @@ -0,0 +1,41 @@ +package hirs.attestationca.persist.entity.userdefined; + +import hirs.attestationca.persist.entity.AbstractEntity; +import jakarta.persistence.Access; +import jakarta.persistence.AccessType; +import jakarta.persistence.Entity; +import jakarta.persistence.Inheritance; +import jakarta.persistence.InheritanceType; + +/** + * A Report represents an integrity report to be appraised by an + * Appraiser. An Appraiser validates the integrity of + * a client's platform with an integrity report. Example reports include an IMA + * report and TPM report. + *

+ * This Report class contains minimal information because each + * report is vastly different. There is an identification number in case the + * Report is stored in a database, and there is a report type. The + * report type is used to determine which Appraisers can appraise + * the report. + */ +@Entity +@Access(AccessType.FIELD) +@Inheritance(strategy = InheritanceType.JOINED) +public abstract class Report extends AbstractEntity { + /** + * Default constructor. + */ + protected Report() { + super(); + } + + /** + * Returns a String that indicates this report type. The report + * type is used to find an Appraiser that can appraise this + * Report. + * + * @return report type + */ + public abstract String getReportType(); +} diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/SupplyChainSettings.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/SupplyChainSettings.java new file mode 100644 index 00000000..59423534 --- /dev/null +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/SupplyChainSettings.java @@ -0,0 +1,123 @@ +package hirs.attestationca.persist.entity.userdefined; + +import hirs.attestationca.persist.entity.UserDefinedEntity; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +/** + * Class represents Supply Chain policy. Supply Chain Policy identifies the methods in + * SupplyChainValidator that should be used in order to validate a supply chain. + * By default, the policy does not enable any validations. + */ +@Table(name = "SupplyChainSettings") +@Getter +@Setter +@Entity +@ToString(callSuper = true) +public class SupplyChainSettings extends UserDefinedEntity { + /** + * Name of the default Supply Chain Policy. + */ + public static final String DEFAULT_POLICY = "Default Supply Chain Policy"; + /** + * Number of days in 10 years. + */ + public static final String TEN_YEARS = "3651"; + /** + * Number of days in 1 year. + */ + public static final String YEAR = "365"; + + @Column(nullable = false, columnDefinition = "boolean default false") + private boolean ecValidationEnabled = false; + + @Column(nullable = false, columnDefinition = "boolean default false") + private boolean pcValidationEnabled = false; + + @Column(nullable = false, columnDefinition = "boolean default false") + private boolean pcAttributeValidationEnabled = false; + + @Column(nullable = false, columnDefinition = "boolean default false") + private boolean firmwareValidationEnabled = false; + + @Column(nullable = false, columnDefinition = "boolean default false") + private boolean utcValidationEnabled = false; + + @Column(nullable = false, columnDefinition = "boolean default false") + private boolean expiredCertificateValidationEnabled = false; + + @Column(nullable = false, columnDefinition = "boolean default false") + private boolean replaceEC = false; + + @Column(nullable = false, columnDefinition = "boolean default true") + private boolean issueAttestationCertificate = true; + + @Column(nullable = false, columnDefinition = "boolean default true") + private boolean issueDevIdCertificate = true; + + @Column(nullable = false) + private String validityDays = TEN_YEARS; + + @Column(nullable = false) + private String devIdValidityDays = TEN_YEARS; + + @Column(nullable = false) + private String reissueThreshold = YEAR; + + @Column(nullable = false) + private String devIdReissueThreshold = YEAR; + + @Column(nullable = false, columnDefinition = "boolean default false") + private boolean generateOnExpiration = false; + + @Column(nullable = false, columnDefinition = "boolean default false") + private boolean devIdExpirationFlag = false; + + @Column(nullable = false, columnDefinition = "boolean default false") + private boolean ignoreImaEnabled = false; + + @Column(nullable = false, columnDefinition = "boolean default false") + private boolean ignoretBootEnabled = false; + + @Column(nullable = false, columnDefinition = "boolean default false") + private boolean linuxOs = false; + + @Column(nullable = false, columnDefinition = "boolean default true") + private boolean ignoreGptEnabled = true; + + @Column(nullable = false, columnDefinition = "boolean default false") + private boolean ignoreOsEvtEnabled = false; + + /** + * Default constructor necessary for Hibernate. + */ + protected SupplyChainSettings() { + super(); + } + + /** + * Constructor used to initialize SupplyChainSettings object. + * + * @param name + * A name used to uniquely identify and reference the Supply Chain policy. + */ + public SupplyChainSettings(final String name) { + super(name); + } + + /** + * Constructor used to initialize SupplyChainSettings object. + * + * @param name + * A name used to uniquely identify and reference the supply chain policy. + * @param description + * Optional description of the policy that can be added by the user + */ + public SupplyChainSettings(final String name, final String description) { + super(name, description); + } +} diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/CertificateAuthorityCredential.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/CertificateAuthorityCredential.java new file mode 100644 index 00000000..72047d42 --- /dev/null +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/CertificateAuthorityCredential.java @@ -0,0 +1,151 @@ +package hirs.attestationca.persist.entity.userdefined.certificate; + +import hirs.attestationca.persist.entity.userdefined.Certificate; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import lombok.Getter; +import org.apache.commons.codec.binary.Hex; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.Arrays; + +/** + * This class persists Certificate Authority credentials by extending the base Certificate + * class with fields unique to CA credentials. + */ +@Entity +public class CertificateAuthorityCredential extends Certificate { + + + @SuppressWarnings("PMD.AvoidUsingHardCodedIP") + private static final String SUBJECT_KEY_IDENTIFIER_EXTENSION = "2.5.29.14"; + + /** + * Holds the name of the 'subjectKeyIdentifier' field. + */ + public static final String SUBJECT_KEY_IDENTIFIER_FIELD = "subjectKeyIdentifier"; + + private static final int CA_BYTE_SIZE = 20; + private static final int PREFIX_BYTE_SIZE = 4; + + @Column + private final byte[] subjectKeyIdentifier; + + @Getter + @Column + private String subjectKeyIdString; + + /** + * this field is part of the TCG CA specification, but has not yet been found in + * manufacturer-provided CAs, and is therefore not currently parsed. + */ + @Getter + @Column + private final String credentialType = "TCPA Trusted Platform Module Endorsement"; + + /** + * Construct a new CertificateAuthorityCredential given its binary contents. The given + * certificate should represent either an X509 certificate or X509 attribute certificate. + * + * @param certificateBytes the contents of a certificate file + * @throws IOException if there is a problem extracting information from the certificate + */ + public CertificateAuthorityCredential(final byte[] certificateBytes) + throws IOException { + super(certificateBytes); + byte[] tempBytes = getX509Certificate() + .getExtensionValue(SUBJECT_KEY_IDENTIFIER_EXTENSION); + + if (tempBytes != null && tempBytes.length > CA_BYTE_SIZE) { + this.subjectKeyIdentifier = truncatePrefixBytes(tempBytes); + } else { + this.subjectKeyIdentifier = + getX509Certificate().getExtensionValue(SUBJECT_KEY_IDENTIFIER_EXTENSION); + } + + if (this.subjectKeyIdentifier != null) { + this.subjectKeyIdString = Hex.encodeHexString(this.subjectKeyIdentifier); + } + } + + /** + * Construct a new CertificateAuthorityCredential by parsing the file at the given path. + * The given certificate should represent either an X509 certificate or X509 attribute + * certificate. + * + * @param certificatePath the path on disk to a certificate + * @throws IOException if there is a problem reading the file + */ + public CertificateAuthorityCredential(final Path certificatePath) + throws IOException { + super(certificatePath); + byte[] tempBytes = getX509Certificate() + .getExtensionValue(SUBJECT_KEY_IDENTIFIER_EXTENSION); + + if (tempBytes.length > CA_BYTE_SIZE) { + this.subjectKeyIdentifier = truncatePrefixBytes(tempBytes); + } else { + this.subjectKeyIdentifier = + getX509Certificate().getExtensionValue(SUBJECT_KEY_IDENTIFIER_EXTENSION); + } + if (this.subjectKeyIdentifier != null) { + this.subjectKeyIdString = Hex.encodeHexString(this.subjectKeyIdentifier); + } + } + + /** + * Default constructor for Hibernate. + */ + protected CertificateAuthorityCredential() { + subjectKeyIdentifier = null; + } + + /** + * @return this certificate's subject key identifier. + */ + public byte[] getSubjectKeyIdentifier() { + if (subjectKeyIdentifier != null) { + return subjectKeyIdentifier.clone(); + } + return null; + } + + private byte[] truncatePrefixBytes(final byte[] certificateBytes) { + byte[] temp = new byte[CA_BYTE_SIZE]; + System.arraycopy(certificateBytes, PREFIX_BYTE_SIZE, temp, 0, CA_BYTE_SIZE); + + return temp; + } + + @Override + @SuppressWarnings("checkstyle:avoidinlineconditionals") + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + if (!super.equals(o)) { + return false; + } + + CertificateAuthorityCredential that = (CertificateAuthorityCredential) o; + +// if (!Objects.equals(credentialType, that.credentialType)) { +// return false; +// } + + return Arrays.equals(subjectKeyIdentifier, that.subjectKeyIdentifier); + } + + @Override + @SuppressWarnings({"checkstyle:magicnumber", "checkstyle:avoidinlineconditionals"}) + public int hashCode() { + int result = super.hashCode(); + result = 31 * result + (credentialType != null ? credentialType.hashCode() : 0); + result = 31 * result + Arrays.hashCode(subjectKeyIdentifier); + return result; + } +} diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/CertificateVariables.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/CertificateVariables.java new file mode 100644 index 00000000..621ef771 --- /dev/null +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/CertificateVariables.java @@ -0,0 +1,47 @@ +package hirs.attestationca.persist.entity.userdefined.certificate; + +public class CertificateVariables { + + public static final String PEM_HEADER = "-----BEGIN CERTIFICATE-----"; + public static final String PEM_FOOTER = "-----END CERTIFICATE-----"; + public static final String PEM_ATTRIBUTE_HEADER = "-----BEGIN ATTRIBUTE CERTIFICATE-----"; + public static final String PEM_ATTRIBUTE_FOOTER = "-----END ATTRIBUTE CERTIFICATE-----"; + public static final String MALFORMED_CERT_MESSAGE = "Malformed certificate detected."; + public static final int MAX_CERT_LENGTH_BYTES = 2048; + public static final int MAX_NUMERIC_PRECISION = 49; // Can store up to 160 bit values + public static final int MAX_PUB_KEY_MODULUS_HEX_LENGTH = 1024; + public static final int KEY_USAGE_BIT0 = 0; + public static final int KEY_USAGE_BIT1 = 1; + public static final int KEY_USAGE_BIT2 = 2; + public static final int KEY_USAGE_BIT3 = 3; + public static final int KEY_USAGE_BIT4 = 4; + public static final int KEY_USAGE_BIT5 = 5; + public static final int KEY_USAGE_BIT6 = 6; + public static final int KEY_USAGE_BIT7 = 7; + public static final int KEY_USAGE_BIT8 = 8; + public static final String KEY_USAGE_DS = "DIGITAL SIGNATURE"; + public static final String KEY_USAGE_NR = "NON-REPUDIATION"; + public static final String KEY_USAGE_KE = "KEY ENCIPHERMENT"; + public static final String KEY_USAGE_DE = "DATA ENCIPHERMENT"; + public static final String KEY_USAGE_KA = "KEY AGREEMENT"; + public static final String KEY_USAGE_KC = "KEY CERT SIGN"; + public static final String KEY_USAGE_CS = "CRL SIGN"; + public static final String KEY_USAGE_EO = "ENCIPHER ONLY"; + public static final String KEY_USAGE_DO = "DECIPHER ONLY"; + public static final String ECDSA_OID = "1.2.840.10045.4.3.2"; + public static final String ECDSA_SHA224_OID = "1.2.840.10045.4.1"; + public static final String RSA256_OID = "1.2.840.113549.1.1.11"; + public static final String RSA384_OID = "1.2.840.113549.1.1.12"; + public static final String RSA512_OID = "1.2.840.113549.1.1.13"; + public static final String RSA224_OID = "1.2.840.113549.1.1.14"; + public static final String RSA512_224_OID = "1.2.840.113549.1.1.15"; + public static final String RSA512_256_OID = "1.2.840.113549.1.1.16"; + public static final String RSA256_STRING = "SHA256WithRSA"; + public static final String RSA384_STRING = "SHA384WithRSA"; + public static final String RSA224_STRING = "SHA224WithRSA"; + public static final String RSA512_STRING = "SHA512WithRSA"; + public static final String RSA512_224_STRING = "SHA512-224WithRSA"; + public static final String RSA512_256_STRING = "SHA512-256WithRSA"; + public static final String ECDSA_STRING = "SHA256WithECDSA"; + public static final String ECDSA_SHA224_STRING = "SHA224WithECDSA"; +} diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/ConformanceCredential.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/ConformanceCredential.java new file mode 100644 index 00000000..25186117 --- /dev/null +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/ConformanceCredential.java @@ -0,0 +1,65 @@ +package hirs.attestationca.persist.entity.userdefined.certificate; + +import hirs.attestationca.persist.entity.userdefined.Certificate; +import jakarta.persistence.Entity; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +import java.io.IOException; +import java.nio.file.Path; + +/** + * This class persists Conformance credentials by extending the base Certificate + * class with fields unique to Conformance credentials. + */ +@NoArgsConstructor(access= AccessLevel.PROTECTED) +@Entity +public class ConformanceCredential extends Certificate { + /** + * This class enables the retrieval of ConformanceCredentials by their attributes. + */ +// public static class Selector extends CertificateSelector { +// /** +// * Construct a new CertificateSelector that will use the given {@link CertificateManager} to +// * retrieve one or many ConformanceCredentials. +// * +// * @param certificateManager the certificate manager to be used to retrieve certificates +// */ +// public Selector(final CertificateManager certificateManager) { +// super(certificateManager, ConformanceCredential.class); +// } +// } + + /** + * Get a Selector for use in retrieving ConformanceCredentials. + * + * @param certMan the CertificateManager to be used to retrieve persisted certificates + * @return a ConformanceCredential.Selector instance to use for retrieving certificates + */ +// public static Selector select(final CertificateManager certMan) { +// return new Selector(certMan); +// } + + /** + * Construct a new ConformanceCredential given its binary contents. The given certificate + * should represent either an X509 certificate or X509 attribute certificate. + * + * @param certificateBytes the contents of a certificate file + * @throws IOException if there is a problem extracting information from the certificate + */ + public ConformanceCredential(final byte[] certificateBytes) throws IOException { + super(certificateBytes); + } + + /** + * Construct a new ConformanceCredential by parsing the file at the given path. The given + * certificate should represent either an X509 certificate or X509 attribute certificate. + * + * @param certificatePath the path on disk to a certificate + * @throws IOException if there is a problem reading the file + */ + public ConformanceCredential(final Path certificatePath) throws IOException { + super(certificatePath); + } + +} diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/DeviceAssociatedCertificate.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/DeviceAssociatedCertificate.java new file mode 100644 index 00000000..948b397c --- /dev/null +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/DeviceAssociatedCertificate.java @@ -0,0 +1,69 @@ +package hirs.attestationca.persist.entity.userdefined.certificate; + +import hirs.attestationca.persist.entity.userdefined.Certificate; +import hirs.attestationca.persist.entity.userdefined.Device; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.MappedSuperclass; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.io.IOException; +import java.nio.file.Path; + +/** + * A Certificate that is associated with a single device. + * + * @see Certificate + */ +@NoArgsConstructor(access= AccessLevel.PACKAGE) +@MappedSuperclass +public abstract class DeviceAssociatedCertificate extends Certificate { + + // a device can have multiple certs of this type. + @Getter + @Setter + @ManyToOne + @JoinColumn(name = "device_id") + private Device device; + + /** + * Holds the name of the entity 'DEVICE_ID' field. + */ + protected static final String DEVICE_ID_FIELD = "device.id"; + + /** + * Construct a new Certificate by parsing the file at the given path. The given certificate + * should represent either an X509 certificate or X509 attribute certificate. + * + * @param certificatePath the path on disk to a certificate + * @throws IOException if there is a problem reading the file + */ + DeviceAssociatedCertificate(final Path certificatePath) throws IOException { + super(certificatePath); + } + + /** + * Construct a new Certificate given its binary contents. The given certificate should + * represent either an X509 certificate or X509 attribute certificate. + * + * @param certificateBytes the contents of a certificate file + * @throws IOException if there is a problem extracting information from the certificate + */ + DeviceAssociatedCertificate(final byte[] certificateBytes) throws IOException { + super(certificateBytes); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(super.toString()); + if (device != null) { + sb.append(String.format("%nDevice -> %s", getDevice().toString())); + } + + return sb.toString(); + } +} diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/EndorsementCredential.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/EndorsementCredential.java new file mode 100644 index 00000000..abacdbfc --- /dev/null +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/EndorsementCredential.java @@ -0,0 +1,652 @@ +package hirs.attestationca.persist.entity.userdefined.certificate; + +import hirs.attestationca.persist.entity.userdefined.certificate.attributes.TPMSecurityAssertions; +import hirs.attestationca.persist.entity.userdefined.certificate.attributes.TPMSpecification; +import jakarta.persistence.Column; +import jakarta.persistence.Embedded; +import jakarta.persistence.Entity; +import jakarta.persistence.Transient; +import lombok.AccessLevel; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.bouncycastle.asn1.ASN1ApplicationSpecific; +import org.bouncycastle.asn1.ASN1BitString; +import org.bouncycastle.asn1.ASN1Boolean; +import org.bouncycastle.asn1.ASN1Encodable; +import org.bouncycastle.asn1.ASN1Enumerated; +import org.bouncycastle.asn1.ASN1GeneralizedTime; +import org.bouncycastle.asn1.ASN1InputStream; +import org.bouncycastle.asn1.ASN1Integer; +import org.bouncycastle.asn1.ASN1Null; +import org.bouncycastle.asn1.ASN1ObjectIdentifier; +import org.bouncycastle.asn1.ASN1OctetString; +import org.bouncycastle.asn1.ASN1Primitive; +import org.bouncycastle.asn1.ASN1Sequence; +import org.bouncycastle.asn1.ASN1Set; +import org.bouncycastle.asn1.ASN1TaggedObject; +import org.bouncycastle.asn1.ASN1UTCTime; +import org.bouncycastle.asn1.DERBMPString; +import org.bouncycastle.asn1.DERExternal; +import org.bouncycastle.asn1.DERGeneralString; +import org.bouncycastle.asn1.DERIA5String; +import org.bouncycastle.asn1.DERNumericString; +import org.bouncycastle.asn1.DERPrintableString; +import org.bouncycastle.asn1.DERT61String; +import org.bouncycastle.asn1.DERTaggedObject; +import org.bouncycastle.asn1.DERUTF8String; +import org.bouncycastle.asn1.DERUniversalString; +import org.bouncycastle.asn1.DERVisibleString; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.math.BigInteger; +import java.nio.file.Path; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.text.ParseException; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +/** + * + * This class persists Certificate Authority credentials by extending the base Certificate + * class with fields unique to Endorsement credentials, as defined in the Trusted + * Computing Group Credential Profiles, specification v.1.2. + * + * trustedcomputinggroup.org/wp-content/uploads/Credential_Profiles_V1.2_Level2_Revision8.pdf + */ +@EqualsAndHashCode(callSuper = false) +@NoArgsConstructor(access= AccessLevel.PROTECTED) +@Entity +public class EndorsementCredential extends DeviceAssociatedCertificate { + + // Indices for ASN1 OBJ items needed for parsing information + private static final int ASN1_OBJ_ID = 0; + private static final int ASN1_OBJ_PRIMITIVE = 1; + private static final int ASN1_FAMILY_INDEX = 0; + private static final int ASN1_LEVEL_INDEX = 1; + private static final int ASN1_REV_INDEX = 2; + private static final int ASN1_VER_INDEX = 0; + private static final int ASN1_UPGRADEABLE_INDEX = 1; + + private static final int EK_LOC_VAL_MIN = 0; + private static final int EK_LOC_VAL_MAX = 2; + private static final int EK_TYPE_VAL_MIN = 0; + private static final int EK_TYPE_VAL_MAX = 3; + + // EK Tag index values + private static final int EK_TYPE_TAG = 0; + private static final int EK_LOC_TAG = 1; + private static final int EK_CERT_LOC_TAG = 2; + + private static final int ASN1_SEQ_UNKNOWN_SIZE = 2; + private static final int ASN1_SEQ_KNOWN_SIZE = 3; + + private static final String TPM_MODEL = "2.23.133.2.2"; + + private static final String TPM_VERSION = "2.23.133.2.3"; + + private static final String TPM_MANUFACTURER = "2.23.133.2.1"; + + private static final String TPM_SPECIFICATION = "2.23.133.2.16"; + + private static final String TPM_SECURITY_ASSERTIONS = "2.23.133.2.18"; + + private static final String CREDENTIAL_TYPE_LABEL = "1.3.6.1.5.5.7.2.2"; + + // number of extra bytes potentially present in a cert header. + private static final int EK_CERT_HEADER_BYTE_COUNT = 7; + + private static final Logger LOG = LogManager.getLogger(EndorsementCredential.class); + + /** + * This class enables the retrieval of EndorsementCredential by their attributes. + */ +// public static class Selector extends CertificateSelector { +// /** +// * Construct a new CertificateSelector that will use the given {@link CertificateManager} to +// * retrieve one or many EndorsementCredentials. +// * +// * @param certificateManager the certificate manager to be used to retrieve certificates +// */ +// public Selector(final CertificateManager certificateManager) { +// super(certificateManager, EndorsementCredential.class); +// } +// +// /** +// * Specify a manufacturer that certificates must have to be considered as matching. +// * @param manufacturer the manufacturer to query, not empty or null +// * @return this instance (for chaining further calls) +// */ +// public Selector byManufacturer(final String manufacturer) { +// setFieldValue(MANUFACTURER_FIELD, manufacturer); +// return this; +// } +// +// /** +// * Specify a model that certificates must have to be considered as matching. +// * @param model the model to query, not empty or null +// * @return this instance (for chaining further calls) +// */ +// public Selector byModel(final String model) { +// setFieldValue(MODEL_FIELD, model); +// return this; +// } +// +// /** +// * Specify a version that certificates must have to be considered as matching. +// * @param version the version to query, not empty or null +// * @return this instance (for chaining further calls) +// */ +// public Selector byVersion(final String version) { +// setFieldValue(VERSION_FIELD, version); +// return this; +// } +// +// /** +// * Specify a device id that certificates must have to be considered +// * as matching. +// * +// * @param device the device id to query +// * @return this instance (for chaining further calls) +// */ +// public Selector byDeviceId(final UUID device) { +// setFieldValue(DEVICE_ID_FIELD, device); +// return this; +// } +// } +// +// /** +// * Get a Selector for use in retrieving EndorsementCredentials. +// * +// * @param certMan the CertificateManager to be used to retrieve persisted certificates +// * @return a EndorsementCredential.Selector instance to use for retrieving certificates +// */ +// public static Selector select(final CertificateManager certMan) { +// return new Selector(certMan); +// } + + /** + * this field is part of the TCG EC specification, but has not yet been found in + * manufacturer-provided ECs, and is therefore not currently parsed + */ + @Getter + @Column + private String credentialType = "TCPA Trusted Platform Module Endorsement"; + + private static final String MANUFACTURER_FIELD = "manufacturer"; + @Getter + @Column + private String manufacturer = null; + + private static final String MODEL_FIELD = "model"; + @Getter + @Column + private String model = null; + + private static final String VERSION_FIELD = "version"; + @Getter + @Column + private String version = null; + + @Getter + @Embedded + private TPMSpecification tpmSpecification = null; + + @Getter + @Embedded + private TPMSecurityAssertions tpmSecurityAssertions = null; //optional + + /* + * this field is part of the TCG EC specification, but has not yet been found in + * manufacturer-provided ECs, and is therefore not currently parsed + */ + @Getter + @Column(nullable = true) + private String policyReference = null; // optional + + /* + * this field is part of the TCG EC specification, but has not yet been found in + * manufacturer-provided ECs, and is therefore not currently parsed + */ + @Getter + @Column(nullable = true) + private String revocationLocator = null; // optional + + @Transient + private Set expectedOids; + + @Transient + private Map parsedFields; + + private static final Logger LOGGER = LogManager.getLogger(EndorsementCredential.class); + + /** + * Construct a new EndorsementCredential given its binary contents. The given + * certificate should represent either an X509 certificate or X509 attribute certificate. + * + * @param certificateBytes the contents of a certificate file + * @throws IOException if there is a problem extracting information from the certificate + */ + public EndorsementCredential(final byte[] certificateBytes) throws IOException { + super(certificateBytes); + parseCertificate(); + } + + /** + * Construct a new EndorsementCredential by parsing the file at the given path. The given + * certificate should represent either an X509 certificate or X509 attribute certificate. + * + * @param certificatePath the path on disk to a certificate + * @throws IOException if there is a problem reading the file + */ + public EndorsementCredential(final Path certificatePath) throws IOException { + this(readBytes(certificatePath)); + } + + /** + * Parses the bytes as an EK. If parsing fails initially, the optionally present header + * is removed and tried again. The cert header, if present, contains some certificate length + * information which isn't needed for parsing. + * @param certificateBytes the bytes of the EC + * @return the EC if a valid credential, null otherwise + */ + public static EndorsementCredential parseWithPossibleHeader(final byte[] certificateBytes) { + + try { + // first, attempt parsing as is + return new EndorsementCredential(certificateBytes); + } catch (Exception e) { + // attempt parsing again after removing extra header bytes. + if (certificateBytes.length <= EK_CERT_HEADER_BYTE_COUNT) { + throw new IllegalArgumentException("EK parsing failed (only one attempt " + + "possible", e); + } + } + + LOG.debug("Attempting parse after removing extra header bytes"); + try { + byte[] truncatedBytes = ArrayUtils.subarray( + certificateBytes, EK_CERT_HEADER_BYTE_COUNT, + certificateBytes.length); + return new EndorsementCredential(truncatedBytes); + } catch (Exception e) { + throw new IllegalArgumentException("Failed to parse EK after multiple attempts", e); + } + } + + /** + * Sets up the OID fields for the parser to search for and prepares a + * hashmap field to hold the discovered values. Must be called once before + * an ASN1Primitive can be parsed. + */ + private void prepareParser() { + expectedOids = new HashSet<>(); + expectedOids.add(TPM_MODEL); + expectedOids.add(TPM_VERSION); + expectedOids.add(TPM_MANUFACTURER); + expectedOids.add(TPM_SPECIFICATION); + expectedOids.add(TPM_SECURITY_ASSERTIONS); + expectedOids.add(CREDENTIAL_TYPE_LABEL); + parsedFields = new HashMap<>(); + } + + /** + * Takes the bytes of an X509 certificate and parses them to extract the relevant fields of an + * Endorsement Credential Certificate. This works by making a single pass through all of the + * ASN1Primitives in the certificate and searches for matching OID keys of specific values. If + * matching OID keys are found, their values are encoded in the fields of the current + * EndorsementCredential object. + * @throws IOException the input certificate bytes were not readable into an X509 + * certificate format + */ + private void parseCertificate() throws IOException { + prepareParser(); + // although we start with a byte representation, we need to change the encoding to + // make it parseable + ASN1InputStream asn1In = null; + try { + X509Certificate ec = super.getX509Certificate(); + asn1In = new ASN1InputStream(ec.getEncoded()); + + ASN1Primitive obj = asn1In.readObject(); + ASN1Sequence seq; + + while (obj != null) { + seq = ASN1Sequence.getInstance(obj); + parseSequence(seq, false, null); + obj = asn1In.readObject(); + } + } catch (CertificateException e) { + throw new IOException("Couldn't read certificate bytes"); + } finally { + if (asn1In != null) { + asn1In.close(); + } + } + + String oid; + Object value; + // unpack fields from parsedFields and set field values + for (Map.Entry entry : parsedFields.entrySet()) { + oid = entry.getKey(); + value = entry.getValue(); + if (oid.equals(TPM_MODEL)) { + model = value.toString(); + LOGGER.debug("Found TPM Model: " + model); + } else if (oid.equals(TPM_VERSION)) { + version = value.toString(); + LOGGER.debug("Found TPM Version: " + version); + } else if (oid.equals(TPM_MANUFACTURER)) { + manufacturer = value.toString(); + LOGGER.debug("Found TPM Manufacturer: " + manufacturer); + } + } + } + + /** + * Parses the ASN1Sequence type by iteratively unpacking each successive element. If, + * however, the method is set to add the sequence to the OID mapping, it may search for + * patterns that correspond to the TPM Security Assertions and TPM Specification and set + * those fields appropriately. + * @param seq the sequence to parse + * @param addToMapping whether or not to store the sequence value as an OID key/value value + * @param key the associated OID key with this value necessary if addToMapping is true + * @throws IOException parsing individual subcomponents failed + */ + private void parseSequence(final ASN1Sequence seq, final boolean addToMapping, + final String key) throws IOException { + // need to check if an OID/Value pair + // it is possible these pairs could be in a larger sequence of size != 2 + // but it appears that all expected TPM related fields are of size 2. + // The other larger ones are only used for generic X509 fields, which we + // don't need to extract here. + if (seq.size() == ASN1_SEQ_UNKNOWN_SIZE) { + ASN1Encodable obj1 = seq.getObjectAt(ASN1_OBJ_ID); + ASN1Encodable obj2 = seq.getObjectAt(ASN1_OBJ_PRIMITIVE); + if (obj1 instanceof ASN1ObjectIdentifier) { + String oid = ((ASN1ObjectIdentifier) obj1).getId(); + if (expectedOids.contains(oid)) { + // parse and put object 2 + parseSingle((ASN1Primitive) obj2, true, oid); + } else { + // there may be subfields that are expected, so continue parsing + parseSingle((ASN1Primitive) obj2, false, null); + } + } + + // The next two are special sequences that have already been matched with an OID. + } else if (addToMapping && key.equals(TPM_SPECIFICATION) + && seq.size() == ASN1_SEQ_KNOWN_SIZE) { + // Parse TPM Specification + DERUTF8String family = (DERUTF8String) seq.getObjectAt(ASN1_FAMILY_INDEX); + ASN1Integer level = (ASN1Integer) seq.getObjectAt(ASN1_LEVEL_INDEX); + ASN1Integer revision = (ASN1Integer) seq.getObjectAt(ASN1_REV_INDEX); + tpmSpecification = new TPMSpecification(family.getString(), level.getValue(), + revision.getValue()); + LOGGER.debug("Found TPM Spec:" + tpmSpecification.toString()); + } else if (addToMapping && key.equals(TPM_SECURITY_ASSERTIONS)) { + // Parse TPM Security Assertions + int seqPosition = 0; + + ASN1Integer ver; + // Parse Security Assertions Version + if (seq.getObjectAt(seqPosition) instanceof ASN1Integer) { + ver = (ASN1Integer) seq.getObjectAt(seqPosition); + seqPosition++; + } else { + // Default value of 1 if field not found + ver = new ASN1Integer(BigInteger.ONE); + } + + ASN1Boolean fieldUpgradeable; + // Parse Security Assertions Field Upgradeable + if (seq.getObjectAt(seqPosition) instanceof ASN1Boolean) { + fieldUpgradeable = (ASN1Boolean) seq.getObjectAt(seqPosition); + seqPosition++; + } else { + // Default value of false if field not found + fieldUpgradeable = ASN1Boolean.getInstance(false); + } + + tpmSecurityAssertions = new TPMSecurityAssertions(ver.getValue(), + fieldUpgradeable.isTrue()); + + LOGGER.debug("Found TPM Assertions: " + tpmSecurityAssertions.toString()); + // Iterate through remaining fields to set optional attributes + int tag; + DERTaggedObject obj; + for (int i = seqPosition; i < seq.size(); i++) { + if (seq.getObjectAt(i) instanceof DERTaggedObject) { + obj = (DERTaggedObject) seq.getObjectAt(i); + tag = obj.getTagNo(); + if (tag == EK_TYPE_TAG) { + int ekGenTypeVal = ((ASN1Enumerated) obj.getObject()).getValue().intValue(); + if (ekGenTypeVal >= EK_TYPE_VAL_MIN && ekGenTypeVal <= EK_TYPE_VAL_MAX) { + TPMSecurityAssertions.EkGenerationType ekGenType + = TPMSecurityAssertions.EkGenerationType.values()[ekGenTypeVal]; + tpmSecurityAssertions.setEkGenType(ekGenType); + } + } else if (tag == EK_LOC_TAG) { + int ekGenLocVal = ((ASN1Enumerated) obj.getObject()).getValue().intValue(); + if (ekGenLocVal >= EK_LOC_VAL_MIN && ekGenLocVal <= EK_LOC_VAL_MAX) { + TPMSecurityAssertions.EkGenerationLocation ekGenLocation + = TPMSecurityAssertions.EkGenerationLocation.values()[ekGenLocVal]; + tpmSecurityAssertions.setEkGenerationLocation(ekGenLocation); + } + } else if (tag == EK_CERT_LOC_TAG) { + int ekCertGenLocVal = ((ASN1Enumerated) obj.getObject()) + .getValue().intValue(); + if (ekCertGenLocVal >= EK_LOC_VAL_MIN + && ekCertGenLocVal <= EK_LOC_VAL_MAX) { + TPMSecurityAssertions.EkGenerationLocation ekCertGenLoc + = TPMSecurityAssertions.EkGenerationLocation. + values()[ekCertGenLocVal]; + tpmSecurityAssertions.setEkGenerationLocation(ekCertGenLoc); + } + } + // ccInfo, fipsLevel, iso9000Certified, and iso9000Uri still to be implemented + } + // Will need additional else if case in the future for instanceof ASN1Boolean when + // supporting TPMSecurityAssertions iso9000Certified field, which could be either + // DERTaggedObject or ASN1Boolean + } + } else { + //parse the elements of the sequence individually + for (ASN1Encodable component : seq) { + parseSingle((ASN1Primitive) component, false, null); + } + } + } + + /** + * Parses the many different types of ASN1Primitives and searches for specific OID + * key/value pairs. Works by traversing the entire ASN1Primitive tree with a single + * pass and populates relevant fields in the EndorsementCredential object. + * @param component the ASN1Primitive to parse + * @param addToMapping whether or not the current component has been matched as the + * value in an expected TPM OID key/value pair + * @param key if addToMapping is true, the key in the OID key/value pair + * @throws IOException parsing of subcomponents in the tree failed. + */ + @SuppressWarnings("checkstyle:methodlength") + private void parseSingle(final ASN1Primitive component, final boolean addToMapping, + final String key) throws IOException { + // null check the key if addToMapping is true + if (addToMapping && StringUtils.isEmpty(key)) { + throw new IllegalArgumentException("Key cannot be empty if adding to field mapping"); + } + + if (component instanceof ASN1Sequence) { + parseSequence((ASN1Sequence) component, addToMapping, key); + + } else if (component instanceof DERUTF8String) { + if (addToMapping) { + DERUTF8String nameData = (DERUTF8String) component; + parsedFields.put(key, nameData.getString()); + } + + } else if (component instanceof ASN1ObjectIdentifier) { + if (addToMapping) { + // shouldn't ever be reached, but just in case + parsedFields.put(key, ((ASN1ObjectIdentifier) component).getId()); + } + + } else if (component instanceof ASN1TaggedObject) { + ASN1TaggedObject taggedObj = (ASN1TaggedObject) component; + parseSingle(taggedObj.getObject(), addToMapping, key); + + } else if (component instanceof ASN1OctetString) { + // this may contain parseable data or may just be a OID key-pair value + ASN1OctetString octStr = (ASN1OctetString) component; + byte[] bytes = octStr.getOctets(); + ByteArrayInputStream inStream = new ByteArrayInputStream(bytes); + ASN1InputStream octIn = new ASN1InputStream(inStream); + try { + ASN1Encodable newComp = octIn.readObject(); + parseSingle((ASN1Primitive) newComp, false, null); + } catch (IOException e) { + // this means octet string didn't contain parsable data, so store the + // value as is + if (addToMapping) { + parsedFields.put(key, bytes); + } + } finally { + if (octIn != null) { + octIn.close(); + } + } + + } else if (component instanceof ASN1Set) { + // all ECs seen to this point use sets differently than sequences and their sets + // don't contain top level OIDs, so we can parse everything term by term, if that + // ceases to be the case, we need to switch to this parsing to be more like + // parseSequences in the future + ASN1Set set = (ASN1Set) component; + Enumeration setContents = set.getObjects(); + ASN1Encodable subComp; + while (setContents.hasMoreElements()) { + subComp = (ASN1Encodable) setContents.nextElement(); + if (subComp instanceof ASN1ObjectIdentifier) { + LOGGER.warn("OID in top level of ASN1Set"); + } + parseSingle((ASN1Primitive) subComp, addToMapping, key); + } + + } else if (component instanceof ASN1Boolean) { + if (addToMapping) { + boolean fieldVal = ((ASN1Boolean) component).isTrue(); + parsedFields.put(key, fieldVal); + } + + } else if (component instanceof ASN1BitString) { + // I don't think this contains more fields and needs to be reparsed, + // though not 100% sure + if (addToMapping) { + byte[] bytes = ((ASN1BitString) component).getBytes(); + parsedFields.put(key, bytes); + } + + } else if (component instanceof ASN1Integer) { + if (addToMapping) { + BigInteger bigInt = ((ASN1Integer) component).getValue(); + parsedFields.put(key, bigInt); + } + + } else if (component instanceof ASN1Null) { + if (addToMapping) { + parsedFields.put(key, null); + } + + } else if (component instanceof ASN1UTCTime) { + if (addToMapping) { + try { + parsedFields.put(key, ((ASN1UTCTime) component).getDate()); + } catch (ParseException pe) { + pe.printStackTrace(); + } + } + + } else if (component instanceof DERPrintableString) { + if (addToMapping) { + parsedFields.put(key, ((DERPrintableString) component).getString()); + } + + } else if (component instanceof ASN1Enumerated) { + if (addToMapping) { + BigInteger value = ((ASN1Enumerated) component).getValue(); + parsedFields.put(key, value); + } + // after about this point, I doubt we'll see any of the following field types, but + // in the interest of completeness and robustness, they are still parsed + } else if (component instanceof DERIA5String) { + if (addToMapping) { + String ia5Str = ((DERIA5String) component).getString(); + parsedFields.put(key, ia5Str); + } + + } else if (component instanceof DERNumericString) { + if (addToMapping) { + String numStr = ((DERNumericString) component).getString(); + parsedFields.put(key, numStr); + } + + } else if (component instanceof ASN1GeneralizedTime) { + if (addToMapping) { + try { + parsedFields.put(key, ((ASN1GeneralizedTime) component).getDate()); + } catch (ParseException e) { + e.printStackTrace(); + } + } + + } else if (component instanceof ASN1ApplicationSpecific) { + parseSingle(((ASN1ApplicationSpecific) component).getObject(), addToMapping, key); + + } else if (component instanceof DERBMPString) { + if (addToMapping) { + String bmpStr = ((DERBMPString) component).getString(); + parsedFields.put(key, bmpStr); + } + + } else if (component instanceof DERExternal) { + parseSingle(((DERExternal) component).getExternalContent(), addToMapping, key); + + } else if (component instanceof DERGeneralString) { + if (addToMapping) { + String generalStr = ((DERGeneralString) component).getString(); + parsedFields.put(key, generalStr); + } + + } else if (component instanceof DERT61String) { + if (addToMapping) { + String t61Str = ((DERT61String) component).getString(); + parsedFields.put(key, t61Str); + } + + } else if (component instanceof DERUniversalString) { + if (addToMapping) { + String univStr = ((DERUniversalString) component).getString(); + parsedFields.put(key, univStr); + } + + } else if (component instanceof DERVisibleString) { + if (addToMapping) { + String visStr = ((DERVisibleString) component).getString(); + parsedFields.put(key, visStr); + } + + } else { + // there are some deprecated types that we don't parse + LOGGER.error("Unparsed type: " + component.getClass()); + } + } +} diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/IssuedAttestationCertificate.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/IssuedAttestationCertificate.java new file mode 100644 index 00000000..d07bbf07 --- /dev/null +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/IssuedAttestationCertificate.java @@ -0,0 +1,105 @@ +package hirs.attestationca.persist.entity.userdefined.certificate; + +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToMany; +import jakarta.persistence.ManyToOne; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.Set; + +/** + * Represents an issued attestation certificate to a HIRS Client. + */ +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@Getter +@Entity +public class IssuedAttestationCertificate extends DeviceAssociatedCertificate { + + /** + * AIC label that must be used. + */ + public static final String AIC_TYPE_LABEL = "TCPA Trusted Platform Identity"; + + @ManyToOne(fetch = FetchType.EAGER) + @JoinColumn(name = "ek_id") + private EndorsementCredential endorsementCredential; + + @ManyToMany(fetch = FetchType.EAGER) + @JoinColumn(name = "pc_id") + private Set platformCredentials; + + /** + * This class enables the retrieval of IssuedAttestationCertificate by their attributes. + */ +// public static class Selector extends CertificateSelector { +// /** +// * Construct a new CertificateSelector that will use the given {@link CertificateManager} to +// * retrieve one or many IssuedAttestationCertificate. +// * +// * @param certificateManager the certificate manager to be used to retrieve certificates +// */ +// public Selector(final CertificateManager certificateManager) { +// super(certificateManager, IssuedAttestationCertificate.class); +// } +// +// /** +// * Specify a device id that certificates must have to be considered +// * as matching. +// * +// * @param device the device id to query +// * @return this instance (for chaining further calls) +// */ +// public Selector byDeviceId(final UUID device) { +// setFieldValue(DEVICE_ID_FIELD, device); +// return this; +// } +// } +// +// /** +// * Get a Selector for use in retrieving IssuedAttestationCertificate. +// * +// * @param certMan the CertificateManager to be used to retrieve persisted certificates +// * @return a IssuedAttestationCertificate.Selector instance to use for retrieving certificates +// */ +// public static IssuedAttestationCertificate.Selector select(final CertificateManager certMan) { +// return new IssuedAttestationCertificate.Selector(certMan); +// } + + + /** + * Constructor. + * @param certificateBytes the issued certificate bytes + * @param endorsementCredential the endorsement credential + * @param platformCredentials the platform credentials + * @throws IOException if there is a problem extracting information from the certificate + */ + public IssuedAttestationCertificate(final byte[] certificateBytes, + final EndorsementCredential endorsementCredential, + final Set platformCredentials) + throws IOException { + super(certificateBytes); + this.endorsementCredential = endorsementCredential; + this.platformCredentials = platformCredentials; + } + + /** + * Constructor. + * @param certificatePath path to certificate + * @param endorsementCredential the endorsement credential + * @param platformCredentials the platform credentials + * @throws IOException if there is a problem extracting information from the certificate + */ + public IssuedAttestationCertificate(final Path certificatePath, + final EndorsementCredential endorsementCredential, + final Set platformCredentials) + throws IOException { + this(readBytes(certificatePath), endorsementCredential, platformCredentials); + } + +} 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 new file mode 100644 index 00000000..0c134b04 --- /dev/null +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/PlatformCredential.java @@ -0,0 +1,796 @@ +package hirs.attestationca.persist.entity.userdefined.certificate; + +import com.google.common.base.Preconditions; +import hirs.attestationca.persist.entity.userdefined.certificate.attributes.ComponentIdentifier; +import hirs.attestationca.persist.entity.userdefined.certificate.attributes.PlatformConfiguration; +import hirs.attestationca.persist.entity.userdefined.certificate.attributes.PlatformConfigurationV1; +import hirs.attestationca.persist.entity.userdefined.certificate.attributes.TBBSecurityAssertion; +import hirs.attestationca.persist.entity.userdefined.certificate.attributes.URIReference; +import hirs.attestationca.persist.entity.userdefined.certificate.attributes.V2.PlatformConfigurationV2; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Transient; +import lombok.AccessLevel; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.extern.log4j.Log4j2; +import org.apache.commons.lang3.ArrayUtils; +import org.apache.logging.log4j.util.Strings; +import org.bouncycastle.asn1.ASN1Encodable; +import org.bouncycastle.asn1.ASN1ObjectIdentifier; +import org.bouncycastle.asn1.ASN1Sequence; +import org.bouncycastle.asn1.DERIA5String; +import org.bouncycastle.asn1.DERNull; +import org.bouncycastle.asn1.x500.AttributeTypeAndValue; +import org.bouncycastle.asn1.x500.RDN; +import org.bouncycastle.asn1.x500.X500Name; +import org.bouncycastle.asn1.x509.AlgorithmIdentifier; +import org.bouncycastle.asn1.x509.Attribute; +import org.bouncycastle.asn1.x509.AttributeCertificate; +import org.bouncycastle.asn1.x509.AttributeCertificateInfo; +import org.bouncycastle.asn1.x509.CertificatePolicies; +import org.bouncycastle.asn1.x509.Extension; +import org.bouncycastle.asn1.x509.GeneralName; +import org.bouncycastle.asn1.x509.GeneralNames; +import org.bouncycastle.asn1.x509.PolicyInformation; +import org.bouncycastle.asn1.x509.PolicyQualifierInfo; +import org.bouncycastle.asn1.x509.UserNotice; +import org.bouncycastle.operator.ContentVerifier; +import org.bouncycastle.operator.ContentVerifierProvider; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * This class persists Platform credentials by extending the base Certificate + * class with fields unique to a Platform credentials, as defined in the Trusted + * Computing Group Credential Profiles, specification v.1.2. + */ +@Getter +@Setter +@Log4j2 +@EqualsAndHashCode(callSuper = false) +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@Entity +public class PlatformCredential extends DeviceAssociatedCertificate { + + private static final int TCG_SPECIFICATION_LENGTH = 3; + // These are Object Identifiers (OIDs) for sections in the credentials + private static final String POLICY_QUALIFIER_CPSURI = "1.3.6.1.5.5.7.2.1"; + private static final String POLICY_QUALIFIER_USER_NOTICE = "1.3.6.1.5.5.7.2.2"; + + // OID for TCG Attributes + private static final String PLATFORM_MANUFACTURER = "2.23.133.2.4"; + private static final String PLATFORM_MODEL = "2.23.133.2.5"; + private static final String PLATFORM_VERSION = "2.23.133.2.6"; + private static final String PLATFORM_SERIAL = "2.23.133.2.23"; + private static final String PLATFORM_BASEBOARD_CHASSIS_COMBINED = "2.23.133.5.1.6"; + + // OID for TCG Platform Class Common Attributes + private static final String PLATFORM_MANUFACTURER_2_0 = "2.23.133.5.1.1"; + private static final String PLATFORM_MODEL_2_0 = "2.23.133.5.1.4"; + private static final String PLATFORM_VERSION_2_0 = "2.23.133.5.1.5"; + private static final String PLATFORM_SERIAL_2_0 = "2.23.133.5.1.6"; + + // OID for Certificate Attributes + private static final String TCG_PLATFORM_SPECIFICATION = "2.23.133.2.17"; + private static final String TPM_SECURITY_ASSERTION = "2.23.133.2.18"; + private static final String TBB_SECURITY_ASSERTION = "2.23.133.2.19"; + private static final String TCG_CREDENTIAL_SPECIFICATION = "2.23.133.2.23"; + private static final String PLATFORM_CONFIGURATION_URI = "2.23.133.5.1.3"; + private static final String PLATFORM_CONFIGURATION = "2.23.133.5.1.7.1"; + private static final String PLATFORM_CONFIGURATION_V2 = "2.23.133.5.1.7.2"; + private static final String PLATFORM_CREDENTIAL_TYPE = "2.23.133.2.25"; + private static final String PLATFORM_BASE_CERT = "2.23.133.8.2"; + private static final String PLATFORM_DELTA_CERT = "2.23.133.8.5"; + + /** + * TCG Platform Specification values + * At this time these are placeholder values. + */ + private static final Map TCG_PLATFORM_MAP = new HashMap() {{ + put("#00000000", "Unclassified"); + put("#00000001", "PC Client"); + put("#00000002", "PDA"); + put("#00000003", "CELLPHONE"); + put("#00000004", "SERVER"); + put("#00000005", "PERIPHERAL"); + put("#00000006", "TSS"); + put("#00000007", "STORAGE"); + put("#00000008", "AUTHENTICATION"); + put("#00000009", "EMBEDDED"); + put("#00000010", "HARD COPY"); + put("#00000011", "INFRASTRUCTURE"); + put("#00000012", "VIRTUALIZATION"); + put("#00000013", "TNC"); + put("#00000014", "MULTI-TENANT"); + }}; + + // number of extra bytes potentially present in a cert header. + private static final int PC_CERT_HEADER_BYTE_COUNT = 8; + + /** + * TCPA Trusted Platform Endorsement. + */ + public static final String CERTIFICATE_TYPE_1_2 = "TCPA Trusted Platform Endorsement"; + + /** + * TCG Trusted Platform Endorsement. + */ + public static final String CERTIFICATE_TYPE_2_0 = "TCG Trusted Platform Endorsement"; + + /** + * This class enables the retrieval of PlatformCredentials by their attributes. + */ +// public static class Selector extends CertificateSelector { +// /** +// * Construct a new CertificateSelector that will use the given {@link CertificateManager} to +// * retrieve one or many PlatformCredentials. +// * +// * @param certificateManager the certificate manager to be used to retrieve certificates +// */ +// public Selector(final CertificateManager certificateManager) { +// super(certificateManager, PlatformCredential.class); +// } +// +// /** +// * Specify a manufacturer that certificates must have to be considered as matching. +// * @param manufacturer the manufacturer to query, not empty or null +// * @return this instance (for chaining further calls) +// */ +// public Selector byManufacturer(final String manufacturer) { +// setFieldValue(MANUFACTURER_FIELD, manufacturer); +// return this; +// } +// +// /** +// * Specify a model that certificates must have to be considered as matching. +// * @param model the model to query, not empty or null +// * @return this instance (for chaining further calls) +// */ +// public Selector byModel(final String model) { +// setFieldValue(MODEL_FIELD, model); +// return this; +// } +// +// /** +// * Specify a version that certificates must have to be considered as matching. +// * @param version the version to query, not empty or null +// * @return this instance (for chaining further calls) +// */ +// public Selector byVersion(final String version) { +// setFieldValue(VERSION_FIELD, version); +// return this; +// } +// +// /** +// * Specify a serial number that certificates must have to be considered as matching. +// * @param serialNumber the serial number to query, not empty or null +// * @return this instance (for chaining further calls) +// */ +// public Selector bySerialNumber(final String serialNumber) { +// setFieldValue(SERIAL_NUMBER_FIELD, serialNumber); +// return this; +// } +// +// /** +// * Specify a board serial number that certificates must have to be considered as matching. +// * @param boardSerialNumber the board serial number to query, not empty or null +// * @return this instance (for chaining further calls) +// */ +// public Selector byBoardSerialNumber(final String boardSerialNumber) { +// setFieldValue(PLATFORM_SERIAL_FIELD, boardSerialNumber); +// return this; +// } +// +// /** +// * Specify a chassis serial number that certificates must have to be considered as matching. +// * @param chassisSerialNumber the board serial number to query, not empty or null +// * @return this instance (for chaining further calls) +// */ +// public Selector byChassisSerialNumber(final String chassisSerialNumber) { +// setFieldValue(CHASSIS_SERIAL_NUMBER_FIELD, chassisSerialNumber); +// return this; +// } +// +// /** +// * Specify a device id that certificates must have to be considered +// * as matching. +// * +// * @param device the device id to query +// * @return this instance (for chaining further calls) +// */ +// public Selector byDeviceId(final UUID device) { +// setFieldValue(DEVICE_ID_FIELD, device); +// return this; +// } +// } + + @Column + private String credentialType = null; + + @Column + private boolean platformBase = false; + + private static final String MANUFACTURER_FIELD = "manufacturer"; + @Column + private String manufacturer = null; + + private static final String MODEL_FIELD = "model"; + @Column + private String model = null; + + private static final String VERSION_FIELD = "version"; + @Column + private String version = null; + + private static final String PLATFORM_SERIAL_FIELD = "platformSerial"; + @Column + private String platformSerial = null; + + private static final String CHASSIS_SERIAL_NUMBER_FIELD = "chassisSerialNumber"; + @Column + private String chassisSerialNumber; + + @Column + private int majorVersion = 0; + + @Column + private int minorVersion = 0; + + @Column + private int revisionLevel = 0; + + @Column + private int tcgCredentialMajorVersion = 0; + + @Column + private int tcgCredentialMinorVersion = 0; + + @Column + private int tcgCredentialRevisionLevel = 0; + + @Column + private String platformClass = null; + + @Column(length = MAX_MESSAGE_LENGTH) + private String componentFailures = Strings.EMPTY; + + @Transient + private EndorsementCredential endorsementCredential = null; + + private String platformChainType = Strings.EMPTY; + private boolean isDeltaChain = false; + + + /** + * Get a Selector for use in retrieving PlatformCredentials. + * + * @param certMan the CertificateManager to be used to retrieve persisted certificates + * @return a PlatformCredential.Selector instance to use for retrieving certificates + */ +// public static Selector select(final CertificateManager certMan) { +// return new Selector(certMan); +// } + + /** + * Construct a new PlatformCredential given its binary contents. ParseFields is + * optionally run. The given certificate should represent either an X509 certificate + * or X509 attribute certificate. + * + * @param certificateBytes the contents of a certificate file + * @param parseFields boolean True to parse fields + * @throws IOException if there is a problem extracting information from the certificate\ + */ + public PlatformCredential(final byte[] certificateBytes, + final boolean parseFields) throws IOException { + super(certificateBytes); + if (parseFields) { + parseFields(); + } + } + + /** + * Construct a new PlatformCredential given its binary contents. The given + * certificate should represent either an X509 certificate or X509 attribute certificate. + * + * @param certificateBytes the contents of a certificate file + * @throws IOException if there is a problem extracting information from the certificate + */ + public PlatformCredential(final byte[] certificateBytes) throws IOException { + this(certificateBytes, true); + } + + /** + * Construct a new PlatformCredential by parsing the file at the given path. The given + * certificate should represent either an X509 certificate or X509 attribute certificate. + * + * @param certificatePath the path on disk to a certificate + * @throws IOException if there is a problem reading the file + */ + public PlatformCredential(final Path certificatePath) throws IOException { + this(readBytes(certificatePath), true); + } + + /** + * Validate the signature on the attribute certificate in this holder. + * + * @param verifierProvider a ContentVerifierProvider that can generate a + * verifier for the signature. + * @return true if the signature is valid, false otherwise. + * @throws IOException if the signature cannot be processed or is inappropriate. + */ + public boolean isSignatureValid(final ContentVerifierProvider verifierProvider) + throws IOException { + AttributeCertificate attCert = getAttributeCertificate(); + AttributeCertificateInfo acinfo = getAttributeCertificate().getAcinfo(); + + // Check if the algorithm identifier is the same + if (!isAlgIdEqual(acinfo.getSignature(), attCert.getSignatureAlgorithm())) { + throw new IOException("signature invalid - algorithm identifier mismatch"); + } + + ContentVerifier verifier; + + try { + // Set ContentVerifier with the signature that will verify + verifier = verifierProvider.get((acinfo.getSignature())); + + } catch (Exception e) { + throw new IOException("unable to process signature: " + e.getMessage(), e); + } + + return verifier.verify(attCert.getSignatureValue().getOctets()); + } + /** + * Parses the bytes as an PC. If parsing fails initially, the optionally present header + * is removed and tried again. The cert header, if present, contains some certificate length + * information which isn't needed for parsing. + * @param certificateBytes the bytes of the PC + * @return the PC if a valid credential, null otherwise + */ + public static PlatformCredential parseWithPossibleHeader(final byte[] certificateBytes) { + PlatformCredential credential = null; + + try { + // first, attempt parsing as is + credential = new PlatformCredential(certificateBytes); + } catch (Exception e) { + // attempt parsing again after removing extra header bytes. + if (certificateBytes.length > PC_CERT_HEADER_BYTE_COUNT) { + log.debug("Attempting parse after removing extra header bytes"); + try { + byte[] truncatedBytes = ArrayUtils.subarray( + certificateBytes, PC_CERT_HEADER_BYTE_COUNT, + certificateBytes.length); + credential = new PlatformCredential(truncatedBytes); + } catch (Exception e1) { + log.warn("Failed to parse PC after multiple attempts", e1); + } + } else { + log.warn("EK parsing failed (only one attempt possible)", e); + } + } + return credential; + } + + private void parseFields() throws IOException { + AttributeCertificateInfo certificate = getAttributeCertificate().getAcinfo(); + Map policyQualifier = getPolicyQualifier(certificate); + credentialType = policyQualifier.get("userNotice"); + + // Parse data based on certificate type (1.2 vs 2.0) + switch (credentialType) { + case CERTIFICATE_TYPE_1_2: + parseAttributeCert(certificate); + break; + case CERTIFICATE_TYPE_2_0: + parseAttributeCert2(certificate); + break; + default: + throw new IOException("Invalid Attribute Credential Type: " + credentialType); + } + + // Get TCG Platform Specification Information + for (ASN1Encodable enc : certificate.getAttributes().toArray()) { + Attribute attr = Attribute.getInstance(enc); + if (attr.getAttrType().toString().equals(TCG_PLATFORM_SPECIFICATION)) { + ASN1Sequence tcgPlatformSpecification + = ASN1Sequence.getInstance(attr.getAttrValues().getObjectAt(0)); + ASN1Sequence tcgSpecificationVersion + = ASN1Sequence.getInstance(tcgPlatformSpecification.getObjectAt(0)); + + this.majorVersion = Integer.parseInt( + tcgSpecificationVersion.getObjectAt(0).toString()); + this.minorVersion = Integer.parseInt( + tcgSpecificationVersion.getObjectAt(1).toString()); + this.revisionLevel = Integer.parseInt( + tcgSpecificationVersion.getObjectAt(2).toString()); + + this.platformClass = tcgPlatformSpecification.getObjectAt(1).toString(); + } else if (attr.getAttrType().toString().equals(PLATFORM_CREDENTIAL_TYPE)) { + ASN1Sequence tcgPlatformType = ASN1Sequence.getInstance( + attr.getAttrValues().getObjectAt(0)); + ASN1ObjectIdentifier platformOid = ASN1ObjectIdentifier.getInstance( + tcgPlatformType.getObjectAt(0)); + + if (platformOid.getId().equals(PLATFORM_BASE_CERT)) { + this.platformBase = true; + this.platformChainType = "Base"; + this.isDeltaChain = true; + } else if (platformOid.getId().equals(PLATFORM_DELTA_CERT)) { + this.platformBase = false; + this.platformChainType = "Delta"; + this.isDeltaChain = true; + } + } + } + } + + /** + * Parse a 1.2 Platform Certificate (Attribute Certificate). + * @param certificate Attribute Certificate + */ + private void parseAttributeCert(final AttributeCertificateInfo certificate) { + Extension subjectAlternativeNameExtension + = certificate.getExtensions().getExtension(Extension.subjectAlternativeName); + // It contains a Subject Alternative Name Extension + if (subjectAlternativeNameExtension != null) { + GeneralNames gnames = GeneralNames.getInstance( + subjectAlternativeNameExtension.getParsedValue()); + for (GeneralName gname : gnames.getNames()) { + // Check if it's a directoryName [4] Name type + if (gname.getTagNo() == GeneralName.directoryName) { + X500Name name = X500Name.getInstance(gname.getName()); + for (RDN rdn: name.getRDNs()) { + for (AttributeTypeAndValue attTV: rdn.getTypesAndValues()) { + switch (attTV.getType().toString()) { + case PLATFORM_MANUFACTURER: + this.manufacturer = attTV.getValue().toString(); + break; + case PLATFORM_MODEL: + this.model = attTV.getValue().toString(); + break; + case PLATFORM_VERSION: + this.version = attTV.getValue().toString(); + break; + case PLATFORM_SERIAL: + this.platformSerial = attTV.getValue().toString(); + break; + case PLATFORM_BASEBOARD_CHASSIS_COMBINED: + String[] combinedValues = attTV.getValue() + .toString() + .split(","); + if (combinedValues.length != 2) { + log.warn("Unable to parse combined " + + "baseboard/chassis SN field"); + } else { + this.chassisSerialNumber = combinedValues[0]; + this.platformSerial = combinedValues[1]; + } + break; + default: + break; + } + } + } + } + } + } + } + + /** + * Parse a 2.0 Platform Certificate (Attribute Certificate). + * @param certificate Attribute Certificate + */ + private void parseAttributeCert2(final AttributeCertificateInfo certificate) + throws IOException { + Extension subjectAlternativeNameExtension + = certificate.getExtensions().getExtension(Extension.subjectAlternativeName); + + // It contains a Subject Alternative Name Extension + if (subjectAlternativeNameExtension != null) { + GeneralNames gnames = GeneralNames.getInstance( + subjectAlternativeNameExtension.getParsedValue()); + for (GeneralName gname : gnames.getNames()) { + // Check if it's a directoryName [4] Name type + if (gname.getTagNo() == GeneralName.directoryName) { + X500Name name = X500Name.getInstance(gname.getName()); + for (RDN rdn: name.getRDNs()) { + for (AttributeTypeAndValue attTV: rdn.getTypesAndValues()) { + switch (attTV.getType().toString()) { + case PLATFORM_MANUFACTURER_2_0: + this.manufacturer = attTV.getValue().toString(); + break; + case PLATFORM_MODEL_2_0: + this.model = attTV.getValue().toString(); + break; + case PLATFORM_VERSION_2_0: + this.version = attTV.getValue().toString(); + break; + case PLATFORM_SERIAL_2_0: + this.platformSerial = attTV.getValue().toString(); + break; + default: + break; + } + } + } + } + } + } + // Get all the attributes map to check for validity + try { + getAllAttributes(); + } catch (IllegalArgumentException ex) { + throw new IOException(ex.getMessage()); + } + } + + /** + * Get the x509 Platform Certificate version. + * @return a big integer representing the certificate version. + */ + @Override + public int getX509CredentialVersion() { + try { + return getAttributeCertificate() + .getAcinfo() + .getVersion() + .getValue().intValue(); + } catch (IOException ex) { + log.warn("X509 Credential Version not found."); + log.error(ex); + return Integer.MAX_VALUE; + } + } + + /** + * Get the cPSuri from the Certificate Policies. + * @return cPSuri from the CertificatePolicies. + * @throws IOException when reading the certificate. + */ + public String getCPSuri() throws IOException { + Map policyQualifier + = getPolicyQualifier(getAttributeCertificate().getAcinfo()); + if (policyQualifier.get("cpsURI") != null && !policyQualifier.get("cpsURI").isEmpty()) { + return policyQualifier.get("cpsURI"); + } + + return null; + } + + /** + * Get the Platform Configuration Attribute from the Platform Certificate. + * @return a map with all the attributes + * @throws IllegalArgumentException when there is a parsing error + * @throws IOException when reading the certificate. + */ + public Map getAllAttributes() + throws IllegalArgumentException, IOException { + Map attributes = new HashMap<>(); + ASN1Sequence attributeSequence; + // Check all attributes for Platform Configuration + for (ASN1Encodable enc: getAttributeCertificate().getAcinfo().getAttributes().toArray()) { + Attribute attr = Attribute.getInstance(enc); + attributeSequence + = ASN1Sequence.getInstance(attr.getAttrValues().getObjectAt(0)); + // Parse sequence based on the attribute OID + switch (attr.getAttrType().getId()) { + case TBB_SECURITY_ASSERTION: + attributes.put("tbbSecurityAssertion", + new TBBSecurityAssertion(attributeSequence)); + break; + case PLATFORM_CONFIGURATION_URI: + attributes.put("platformConfigurationURI", + new URIReference(attributeSequence)); + break; + case PLATFORM_CONFIGURATION: + attributes.put("platformConfiguration", + new PlatformConfigurationV1(attributeSequence)); + break; + case PLATFORM_CONFIGURATION_V2: + attributes.put("platformConfiguration", + new PlatformConfigurationV2(attributeSequence)); + break; + case TCG_PLATFORM_SPECIFICATION: + case PLATFORM_CREDENTIAL_TYPE: + // handled in parseFields + break; + case TCG_CREDENTIAL_SPECIFICATION: + getTCGCredentialSpecification(attributeSequence); + break; + default: + // No class defined for this attribute + log.warn("No class defined for attribute with OID: " + + attr.getAttrType().getId()); + break; + } + } + return attributes; + } + + /** + * Get the specified attribute from the Platform Certificate. + * @param attributeName to retrieve from the map. + * @return an Object with the attribute. + * @throws IllegalArgumentException when there is a parsing error + * @throws IOException when reading the certificate. + */ + public Object getAttribute(final String attributeName) + throws IllegalArgumentException, IOException { + return getAllAttributes().get(attributeName); + } + + /** + * Get the Platform Configuration Attribute from the Platform Certificate. + * @return a map with the Platform Configuration information. + * @throws IllegalArgumentException when there is a parsing error + * @throws IOException when reading the certificate. + */ + public PlatformConfiguration getPlatformConfiguration() + throws IllegalArgumentException, IOException { + + if (getAttribute("platformConfiguration") != null + && getAttribute("platformConfiguration") instanceof PlatformConfiguration) { + return (PlatformConfiguration) getAttribute("platformConfiguration"); + } + + return null; + } + + /** + * Get the Platform Configuration URI Attribute from the Platform Certificate. + * @return an URIReference object to the Platform Configuration URI. + * @throws IllegalArgumentException when there is a parsing error + * @throws IOException when reading the certificate. + */ + public URIReference getPlatformConfigurationURI() + throws IllegalArgumentException, IOException { + if (getAttribute("platformConfigurationURI") != null + && getAttribute("platformConfigurationURI") instanceof URIReference) { + return (URIReference) getAttribute("platformConfigurationURI"); + } + return null; + } + + /** + * Get the TBB Security Assertion from the Platform Certificate. + * @return a TBBSecurityAssertion object. + * @throws IllegalArgumentException when there is a parsing error + * @throws IOException when reading the certificate. + */ + public TBBSecurityAssertion getTBBSecurityAssertion() + throws IllegalArgumentException, IOException { + if (getAttribute("tbbSecurityAssertion") != null + && getAttribute("tbbSecurityAssertion") instanceof TBBSecurityAssertion) { + return (TBBSecurityAssertion) getAttribute("tbbSecurityAssertion"); + } + return null; + } + + /** + * This method sets the TCG Credential fields from a certificate, if provided. + * + * @param attributeSequence The sequence associated with 2.23.133.2.23 + */ + private void getTCGCredentialSpecification(final ASN1Sequence attributeSequence) { + try { + this.tcgCredentialMajorVersion = Integer.parseInt( + attributeSequence.getObjectAt(0).toString()); + this.tcgCredentialMinorVersion = Integer.parseInt( + attributeSequence.getObjectAt(1).toString()); + this.tcgCredentialRevisionLevel = Integer.parseInt( + attributeSequence.getObjectAt(2).toString()); + } catch (NumberFormatException nfEx) { + // ill-formed ASN1 + String fieldContents = attributeSequence.toString(); + + if (fieldContents != null && fieldContents.contains(",")) { + fieldContents = fieldContents.replaceAll("[^a-zA-Z0-9,]", ""); + String[] fields = fieldContents.split(","); + + if (fields.length == TCG_SPECIFICATION_LENGTH) { + this.tcgCredentialMajorVersion = Integer.parseInt(fields[0]); + this.tcgCredentialMinorVersion = Integer.parseInt(fields[1]); + this.tcgCredentialRevisionLevel = Integer.parseInt(fields[2]); + } + } + } + } + + /** + * Get the list of component identifiers if there are any. + * @return the list of component identifiers if there are any + */ + public List getComponentIdentifiers() { + try { + PlatformConfiguration platformConfig = getPlatformConfiguration(); + if (platformConfig != null) { + return platformConfig.getComponentIdentifier(); + } + } catch (IOException e) { + log.error("Unable to parse Platform Configuration from Credential or find" + + "component identifiers"); + } + return Collections.emptyList(); + } + + /** + * Verify if the AlgorithmIdentifiers are equal. + * + * @param id1 AlgorithIdentifier one + * @param id2 AlgorithIdentifier two + * @return True if are the same, False if not + */ + public static boolean isAlgIdEqual(final AlgorithmIdentifier id1, + final AlgorithmIdentifier id2) { + if (!id1.getAlgorithm().equals(id2.getAlgorithm())) { + return false; + } + if (id1.getParameters() == null) { + if (id2.getParameters() != null && !id2.getParameters().equals(DERNull.INSTANCE)) { + return false; + } + return true; + } + if (id2.getParameters() == null) { + if (id1.getParameters() != null && !id1.getParameters().equals(DERNull.INSTANCE)) { + return false; + } + return true; + } + return id1.getParameters().equals(id2.getParameters()); + } + + /** + * Get the PolicyQualifier from the Certificate Policies Extension. + * + * @param certificate Attribute Certificate information + * @return Policy Qualifier from the Certificate Policies Extension + */ + public static Map getPolicyQualifier( + final AttributeCertificateInfo certificate) { + Preconditions.checkArgument(certificate.getExtensions() != null, + "Platform certificate should have extensions."); + + CertificatePolicies certPolicies + = CertificatePolicies.fromExtensions(certificate.getExtensions()); + Map policyQualifiers = new HashMap<>(); + String userNoticeQualifier = ""; + String cpsURI = ""; + + if (certPolicies != null) { + // Must contain at least one Policy + for (PolicyInformation policy : certPolicies.getPolicyInformation()) { + for (ASN1Encodable pQualifierInfo: policy.getPolicyQualifiers().toArray()) { + PolicyQualifierInfo info = PolicyQualifierInfo.getInstance(pQualifierInfo); + // Subtract the data based on the OID + switch (info.getPolicyQualifierId().getId()) { + case POLICY_QUALIFIER_CPSURI: + cpsURI = DERIA5String.getInstance(info.getQualifier()).getString(); + break; + case POLICY_QUALIFIER_USER_NOTICE: + UserNotice userNotice = UserNotice.getInstance(info.getQualifier()); + userNoticeQualifier = userNotice.getExplicitText().getString(); + break; + default: + break; + } + } + } + } + + // Add to map + policyQualifiers.put("userNotice", userNoticeQualifier); + policyQualifiers.put("cpsURI", cpsURI); + + return policyQualifiers; + } +} diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/attributes/CommonCriteriaMeasures.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/attributes/CommonCriteriaMeasures.java new file mode 100644 index 00000000..401a9383 --- /dev/null +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/attributes/CommonCriteriaMeasures.java @@ -0,0 +1,300 @@ +package hirs.attestationca.persist.entity.userdefined.certificate.attributes; + +import lombok.Getter; +import lombok.Setter; +import org.bouncycastle.asn1.ASN1Boolean; +import org.bouncycastle.asn1.ASN1Enumerated; +import org.bouncycastle.asn1.ASN1ObjectIdentifier; +import org.bouncycastle.asn1.ASN1Sequence; +import org.bouncycastle.asn1.ASN1TaggedObject; +import org.bouncycastle.asn1.DERIA5String; + +/** + * Basic class that handle CommonCriteriaMeasures for the Platform Certificate + * Attribute. + *

+ * CommonCriteriaMeasures ::= SEQUENCE {
+ *      version IA5STRING (SIZE (1..STRMAX)), "2.2" or "3.1";
+ *      assurancelevel EvaluationAssuranceLevel,
+ *      evaluationStatus EvaluationStatus,
+ *      plus BOOLEAN DEFAULT FALSE,
+ *      strengthOfFunction [0] IMPLICIT StrengthOfFunction OPTIONAL,
+ *      profileOid [1] IMPLICIT OBJECT IDENTIFIER OPTIONAL,
+ *      profileUri [2] IMPLICIT URIReference OPTIONAL,
+ *      targetOid [3] IMPLICIT OBJECT IDENTIFIER OPTIONAL,
+ *      targetUri [4] IMPLICIT URIReference OPTIONAL }
+ * 
+ */ +@Getter @Setter +public class CommonCriteriaMeasures { + + private static final int STRENGTH_OF_FUNCTION = 0; + private static final int PROFILE_OID = 1; + private static final int PROFILE_URI = 2; + private static final int TARGET_OID = 3; + private static final int TARGET_URI = 4; + + /** + * A type to handle the evaluation status used in the Common Criteria Measurement. + * Ordering of enum types is intentional and their ordinal values correspond to enum + * values in the TCG spec. + * + *
+     * EvaluationStatus ::= ENUMERATED {
+     *      designedToMeet (0),
+     *      evaluationInProgress (1),
+     *      evaluationCompleted (2) }
+     * 
+ */ + public enum EvaluationStatus { + /** + * Evaluation designed to meet. + */ + DESIGNEDTOMEET("designed To Meet"), + /** + * Evaluation in progress. + */ + EVALUATIONINPROGRESS("evaluation In Progress"), + /** + * Evaluation completed. + */ + EVALUATIONCOMPLETED("evaluation Completed"); + + @Getter + private final String value; + + /** + * Basic constructor. + * @param value string containing the value. + */ + EvaluationStatus(final String value) { + this.value = value; + } + } + + /** + * A type to handle the strength of function used in the Common Criteria Measurement. + * Ordering of enum types is intentional and their ordinal values correspond to enum + * values in the TCG spec. + * + *
+     * StrengthOfFunction ::= ENUMERATED {
+     *      basic (0),
+     *      medium (1),
+     *      high (2) }
+     * 
+ */ + public enum StrengthOfFunction { + /** + * Basic function. + */ + BASIC("basic"), + /** + * Medium function. + */ + MEDIUM("medium"), + /** + * Hight function. + */ + HIGH("high"); + + @Getter + private final String value; + + /** + * Basic constructor. + * @param value string containing the value. + */ + StrengthOfFunction(final String value) { + this.value = value; + } + } + + /** + * A type to handle the evaluation assurance aevel used in the Common Criteria Measurement. + * Ordering of enum types is intentional and their ordinal values correspond to enum + * values in the TCG spec. + * + *
+     * EvaluationAssuranceLevel ::= ENUMERATED {
+     *      levell (1),
+     *      level2 (2),
+     *      level3 (3),
+     *      level4 (4),
+     *      level5 (5),
+     *      level6 (6),
+     *      level7 (7) }
+     * 
+ */ + public enum EvaluationAssuranceLevel { + /** + * Evaluation Assurance Level 1. + */ + LEVEL1("level 1"), + /** + * Evaluation Assurance Level 2. + */ + LEVEL2("level 2"), + /** + * Evaluation Assurance Level 3. + */ + LEVEL3("level 3"), + /** + * Evaluation Assurance Level 4. + */ + LEVEL4("level 4"), + /** + * Evaluation Assurance Level 5. + */ + LEVEL5("level 5"), + /** + * Evaluation Assurance Level 6. + */ + LEVEL6("level 6"), + /** + * Evaluation Assurance Level 7. + */ + LEVEL7("level 7"); + + @Getter + private final String value; + /** + * Basic constructor. + * @param value string containing the value. + */ + EvaluationAssuranceLevel(final String value) { + this.value = value; + } + } + + private DERIA5String version; + private EvaluationAssuranceLevel assuranceLevel; + private EvaluationStatus evaluationStatus; + private ASN1Boolean plus; + private StrengthOfFunction strengthOfFunction; + private ASN1ObjectIdentifier profileOid; + private URIReference profileUri; + private ASN1ObjectIdentifier targetOid; + private URIReference targetUri; + + /** + * Default constructor. + */ + public CommonCriteriaMeasures() { + this.version = null; + this.assuranceLevel = null; + this.evaluationStatus = null; + this.plus = ASN1Boolean.FALSE; + this.strengthOfFunction = null; + this.profileOid = null; + this.profileUri = null; + this.targetOid = null; + this.targetUri = null; + } + + /** + * Constructor given the SEQUENCE that contains Common Criteria Measures. + * @param sequence containing the the common criteria measures + * @throws IllegalArgumentException if there was an error on the parsing + */ + public CommonCriteriaMeasures(final ASN1Sequence sequence) throws IllegalArgumentException { + + //Get all the mandatory values + int index = 0; + version = DERIA5String.getInstance(sequence.getObjectAt(index)); + ++index; + ASN1Enumerated enumarated = ASN1Enumerated.getInstance(sequence.getObjectAt(index)); + ++index; + //Throw exception when is not between 1 and 7 + if (enumarated.getValue().intValue() <= 0 + || enumarated.getValue().intValue() > EvaluationAssuranceLevel.values().length) { + throw new IllegalArgumentException("Invalid assurance level."); + } + assuranceLevel = EvaluationAssuranceLevel.values()[enumarated.getValue().intValue() - 1]; + enumarated = ASN1Enumerated.getInstance(sequence.getObjectAt(index)); + ++index; + evaluationStatus = EvaluationStatus.values()[enumarated.getValue().intValue()]; + //Default plus value + plus = ASN1Boolean.FALSE; + + //Current sequence index + if (sequence.getObjectAt(index).toASN1Primitive() instanceof ASN1Boolean) { + plus = ASN1Boolean.getInstance(sequence.getObjectAt(index)); + index++; + } + + //Optional values (default to null or empty) + strengthOfFunction = null; + profileOid = null; + profileUri = null; + targetOid = null; + targetUri = null; + + //Sequence for the URIReference + ASN1Sequence uriSequence; + + //Continue reading the sequence + for (; index < sequence.size(); index++) { + ASN1TaggedObject taggedObj = ASN1TaggedObject.getInstance(sequence.getObjectAt(index)); + switch (taggedObj.getTagNo()) { + case STRENGTH_OF_FUNCTION: + enumarated = ASN1Enumerated.getInstance(taggedObj, false); + strengthOfFunction + = StrengthOfFunction.values()[enumarated.getValue().intValue()]; + break; + case PROFILE_OID: + profileOid = ASN1ObjectIdentifier.getInstance(taggedObj, false); + break; + case PROFILE_URI: + uriSequence = ASN1Sequence.getInstance(taggedObj, false); + profileUri = new URIReference(uriSequence); + break; + case TARGET_OID: + targetOid = ASN1ObjectIdentifier.getInstance(taggedObj, false); + break; + case TARGET_URI: + uriSequence = ASN1Sequence.getInstance(taggedObj, false); + targetUri = new URIReference(uriSequence); + break; + default: + throw new IllegalArgumentException("Common criteria measures contains " + + "invalid tagged object."); + } + } + } + + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("ComponentIdentifier{"); + sb.append("version=").append(version.toString()); + sb.append(", assuranceLevel=").append(assuranceLevel.getValue()); + sb.append(", evaluationStatus=").append(evaluationStatus.getValue()); + sb.append(", plus=").append(plus.toString()); + //Not null optional objects + sb.append(", strengthOfFunction="); + if (strengthOfFunction != null) { + sb.append(strengthOfFunction.getValue()); + } + sb.append(", profileOid="); + if (profileOid != null) { + sb.append(profileOid.getId()); + } + sb.append(", profileUri="); + if (profileUri != null) { + sb.append(profileUri.toString()); + } + sb.append(", targetOid="); + if (targetOid != null) { + sb.append(targetOid.getId()); + } + sb.append(", targetUri="); + if (targetUri != null) { + sb.append(targetUri.toString()); + } + sb.append("}"); + + return sb.toString(); + } +} diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/attributes/ComponentAddress.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/attributes/ComponentAddress.java new file mode 100644 index 00000000..8b5a47b2 --- /dev/null +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/attributes/ComponentAddress.java @@ -0,0 +1,91 @@ +package hirs.attestationca.persist.entity.userdefined.certificate.attributes; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.Setter; +import org.bouncycastle.asn1.ASN1ObjectIdentifier; +import org.bouncycastle.asn1.ASN1Sequence; +import org.bouncycastle.asn1.DERUTF8String; + +/** + * Basic class that handle component addresses from the component identifier. + *
+ * componentAddress ::= SEQUENCE {
+ *      addressType AddressType,
+ *      addressValue UTF8String (SIZE (1..STRMAX)) }
+ * where STRMAX is 256
+ * 
+ */ +@Getter +@Setter +@AllArgsConstructor +public class ComponentAddress { + + /** + * Number of identifiers that a component address must have. + */ + public static final int IDENTIFIER_NUMBER = 2; + + private static final String ETHERNET_MAC = "2.23.133.17.1"; + private static final String WLAN_MAC = "2.23.133.17.2"; + private static final String BLUETOOTH_MAC = "2.23.133.17.3"; + + private ASN1ObjectIdentifier addressType; + private DERUTF8String addressValue; + + /** + * Default constructor. + */ + public ComponentAddress() { + addressType = null; + addressValue = null; + } + + /** + * Constructor given the SEQUENCE that contains the type and value for the + * component address. + * + * @param sequence containing the type and value for the component address + * @throws IllegalArgumentException if there was an error on the parsing + */ + public ComponentAddress(final ASN1Sequence sequence) throws IllegalArgumentException { + //Check if the sequence contains the two values required + if (sequence.size() != IDENTIFIER_NUMBER) { + throw new IllegalArgumentException("Component address does not contain " + + "all the required fields."); + } + addressType = ASN1ObjectIdentifier.getInstance(sequence.getObjectAt(0)); + addressValue = DERUTF8String.getInstance(sequence.getObjectAt(1)); + } + + /** + * Get the string value for the address type. + * @return the string value for the address type + */ + public String getAddressTypeValue() { + String typeValue; + switch (this.addressType.getId()) { + case ETHERNET_MAC: + typeValue = "ethernet mac"; + break; + case WLAN_MAC: + typeValue = "wlan mac"; + break; + case BLUETOOTH_MAC: + typeValue = "bluetooth mac"; + break; + default: + typeValue = "unknown mac"; + break; + } + return typeValue; + } + + @Override + public String toString() { + return "ComponentAddress{" + + "addressType=" + addressType.getId() + + ", addressValue=" + addressValue.getString() + + '}'; + } +} 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 new file mode 100644 index 00000000..40ae1763 --- /dev/null +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/attributes/ComponentClass.java @@ -0,0 +1,248 @@ +package hirs.attestationca.persist.entity.userdefined.certificate.attributes; + +import com.eclipsesource.json.JsonObject; +import com.eclipsesource.json.JsonObject.Member; +import hirs.utils.JsonUtils; +import lombok.Getter; + +import java.nio.file.FileSystems; +import java.nio.file.Path; + +/** + *

+ * This class parses the associated component identifier located in Platform + * Certificates and maps them to the corresponding string representation found + * in the associated JSON file. If the value can not be found, either because + * the provided value is malformed or doesn't exist in the mapping, then values + * returned will not match what is expected. This class will return Unknown as a + * category and None as the component which is not a valid mapping. This is + * because None is a category and Unknown is a component identifier. + *

+ *
+ *   componentClass ::= SEQUENCE {
+ *       componentClassRegistry ComponentClassRegistry,
+ *       componentClassValue OCTET STRING SIZE(4) ) }
+ * 
+ * + * A note for the future. + */ +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"); + +// private static final Path JSON_PATH = FileSystems.getDefault() +// .getPath("/opt", "hirs", "default-properties", "component-class.json"); + private static final String OTHER_STRING = "Other"; + private static final String UNKNOWN_STRING = "Unknown"; + private static final String NONE_STRING = "None"; + + // Used to indicate that the component string value provided is erroneous + private static final String ERROR = "-1"; + private static final int MID_INDEX = 4; + /** + * All TCG categories have Other and Unknown as the first 2 values. + */ + private static final String OTHER = "0000"; + private static final String UNKNOWN = "0001"; + + @Getter + private String category, categoryStr; + @Getter + private String component, componentStr; + private String registryType; + private String componentIdentifier; + + /** + * Default class constructor. + */ + public ComponentClass() { + this("TCG", JSON_PATH, UNKNOWN); + } + + /** + * Class Constructor that takes a String representation of the component + * value. + * + * @param registryOid the decimal notation for the type of registry + * @param componentIdentifier component value + */ + public ComponentClass(final String registryOid, final String componentIdentifier) { + this(registryOid, JSON_PATH, componentIdentifier); + } + + /** + * Class Constructor that takes a String representation of the component + * value. + * + * @param componentClassPath file path for the json + * @param componentIdentifier component value + */ + public ComponentClass(final Path componentClassPath, final String componentIdentifier) { + this(TCG_COMPONENT_REGISTRY, componentClassPath, componentIdentifier); + } + + /** + * Main Class Constructor that takes in an integer representation of the + * component value. Sets main class variables to default values and then + * matches the value against defined values in the associated JSON file. + * + * @param registryOid the decimal notation for the type of registry + * @param componentClassPath file path for the json + * @param componentIdentifier component value + */ + public ComponentClass(final String registryOid, + final Path componentClassPath, + final String componentIdentifier) { + this.category = OTHER; + this.component = NONE_STRING; + if (componentIdentifier == null || componentIdentifier.isEmpty()) { + this.componentIdentifier = ""; + } else { + this.componentIdentifier = verifyComponentValue(componentIdentifier); + } + + switch (registryOid) { + case TCG_COMPONENT_REGISTRY -> registryType = "TCG"; + case SMBIOS_COMPONENT_REGISTRY -> registryType = "SMBIOS"; + default -> registryType = UNKNOWN_STRING; + } + + switch (this.componentIdentifier) { + case OTHER: + this.categoryStr = NONE_STRING; + this.component = OTHER; + this.componentStr = OTHER_STRING; + break; + case UNKNOWN: + case "": + this.categoryStr = NONE_STRING; + this.component = UNKNOWN; + this.componentStr = UNKNOWN_STRING; + break; + case ERROR: + // Number Format Exception + break; + default: + this.category = this.componentIdentifier.substring(0, MID_INDEX) + this.category; + this.component = OTHER + this.componentIdentifier.substring(MID_INDEX); + findStringValues(JsonUtils.getSpecificJsonObject(componentClassPath, registryType)); + break; + } + } + + /** + * This is the main way this class will be referenced and how it + * will be displayed on the portal. + * @return String combination of category and component. + */ + @Override + public String toString() { + String resultString; + if (componentStr.equals(UNKNOWN_STRING) || component.equals(OTHER_STRING)) { + resultString = String.format("%s%n%s", registryType, categoryStr); + } else { + resultString = String.format("%s%n%s - %s", registryType, categoryStr, componentStr); + } + return resultString; + } + + /** + * Getter for the Category mapped to the associated value in. + * + * @param categories a JSON object associated with mapped categories in file + * {}@link componentIdentifier}. + */ + private void findStringValues(final JsonObject categories) { + String categoryID; + String componentMask; + boolean found = false; + + if (categories != null) { + for (String name : categories.names()) { + categoryID = verifyComponentValue(categories.get(name) + .asObject().get("ID").asString()); + componentMask = componentIdentifier.substring(MID_INDEX); + // check for the correct flag + if (categoryMatch(componentIdentifier.substring(0, MID_INDEX), + categoryID.substring(0, MID_INDEX))) { + found = true; + JsonObject componentTypes = categories.get(name) + .asObject().get("Types").asObject(); + categoryStr = name; + + switch (componentMask) { + case OTHER -> componentStr = OTHER_STRING; + case UNKNOWN -> componentStr = UNKNOWN_STRING; + default -> getComponent(componentTypes); + } + } + } + } + + if (!found) { + this.categoryStr = NONE_STRING; + this.componentStr = UNKNOWN_STRING; + } + } + + /** + * Returns the value of the comparison between a category and the what's in the id. + * @param category the category to compare + * @param componentId the id value to compare + * @return true if they match + */ + public boolean categoryMatch(final String category, final String componentId) { + return category.equals(componentId); + } + + /** + * Getter for the component associated with the component JSON Object mapped + * in the JSON file. + * + * @param components JSON Object for the categories components + */ + private void getComponent(final JsonObject components) { + String typeID; + + if (components != null) { + for (Member member : components) { + typeID = verifyComponentValue(member.getName()); + + if (component.equals(typeID)) { + componentStr = member.getValue().asString(); + } + } + } + } + + /** + * This method converts the string representation of the component ID into + * an integer. Or throws and error if the format is in error. + * + * @param component string representation of the component ID + * @return the int representation of the component + */ + private static String verifyComponentValue(final String component) { + String componentValue = ERROR; + + if (component != null) { + try { + if (component.contains("x")) { + componentValue = component.substring(component.indexOf("x") + 1); + } else { + if (component.contains("#")) { + componentValue = component.replace("#", ""); + } else { + return component; + } + } + } catch (NumberFormatException nfEx) { + //invalid entry + } + } + + return componentValue; + } +} diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/attributes/ComponentIdentifier.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/attributes/ComponentIdentifier.java new file mode 100644 index 00000000..b6aa5c72 --- /dev/null +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/attributes/ComponentIdentifier.java @@ -0,0 +1,238 @@ +package hirs.attestationca.persist.entity.userdefined.certificate.attributes; + + +import lombok.AllArgsConstructor; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; +import org.apache.commons.lang3.StringUtils; +import org.bouncycastle.asn1.ASN1Boolean; +import org.bouncycastle.asn1.ASN1ObjectIdentifier; +import org.bouncycastle.asn1.ASN1Sequence; +import org.bouncycastle.asn1.ASN1TaggedObject; +import org.bouncycastle.asn1.DERUTF8String; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +/** + * Basic class that handle component identifiers from the Platform Configuration + * Attribute. + *
+ * ComponentIdentifier ::= SEQUENCE {
+ *      componentManufacturer UTF8String (SIZE (1..STRMAX)),
+ *      componentModel UTF8String (SIZE (1..STRMAX)),
+ *      componentSerial[0] IMPLICIT UTF8String (SIZE (1..STRMAX)) OPTIONAL,
+ *      componentRevision [1] IMPLICIT UTF8String (SIZE (1..STRMAX)) OPTIONAL,
+ *      componentManufacturerId [2] IMPLICIT PrivateEnterpriseNumber OPTIONAL,
+ *      fieldReplaceable [3] IMPLICIT BOOLEAN OPTIONAL,
+ *      componentAddress [4] IMPLICIT
+ *          SEQUENCE(SIZE(1..CONFIGMAX)) OF ComponentAddress OPTIONAL}
+ * where STRMAX is 256, CONFIGMAX is 32
+ * 
+ */ +@Getter +@Setter +@AllArgsConstructor +@EqualsAndHashCode +public class ComponentIdentifier { + + /** + * Variable for components that aren't set. + */ + public static final String EMPTY_COMPONENT = "[Empty]"; + /** + * Variable for components that aren't set. + */ + public static final String NOT_SPECIFIED_COMPONENT = "Not Specified"; + /** + * Maximum number of configurations. + */ + public static final int CONFIGMAX = 32; + + private static final int MANDATORY_ELEMENTS = 2; + // optional sequence objects + /** + * Static variable indicated array position for the serial number. + */ + protected static final int COMPONENT_SERIAL = 0; + /** + * Static variable indicated array position for the revision info. + */ + protected static final int COMPONENT_REVISION = 1; + /** + * Static variable indicated array position for the manufacturer id. + */ + protected static final int COMPONENT_MANUFACTURER_ID = 2; + /** + * Static variable indicated array position for the field replaceable value. + */ + protected static final int FIELD_REPLACEABLE = 3; + /** + * Static variable indicated array position for the component address. + */ + protected static final int COMPONENT_ADDRESS = 4; + + private DERUTF8String componentManufacturer; + private DERUTF8String componentModel; + private DERUTF8String componentSerial; + private DERUTF8String componentRevision; + private ASN1ObjectIdentifier componentManufacturerId; + private ASN1Boolean fieldReplaceable; + private List componentAddress; + private boolean validationResult = true; + + /** + * Default constructor. + */ + public ComponentIdentifier() { + componentManufacturer = new DERUTF8String(NOT_SPECIFIED_COMPONENT); + componentModel = new DERUTF8String(NOT_SPECIFIED_COMPONENT); + componentSerial = new DERUTF8String(StringUtils.EMPTY); + componentRevision = new DERUTF8String(StringUtils.EMPTY); + componentManufacturerId = null; + fieldReplaceable = null; + componentAddress = new ArrayList<>(); + } + + /** + * Constructor given the components values. + * + * @param componentManufacturer represents the component manufacturer + * @param componentModel represents the component model + * @param componentSerial represents the component serial number + * @param componentRevision represents the component revision + * @param componentManufacturerId represents the component manufacturer ID + * @param fieldReplaceable represents if the component is replaceable + * @param componentAddress represents a list of addresses + */ + public ComponentIdentifier(final DERUTF8String componentManufacturer, + final DERUTF8String componentModel, + final DERUTF8String componentSerial, + final DERUTF8String componentRevision, + final ASN1ObjectIdentifier componentManufacturerId, + final ASN1Boolean fieldReplaceable, + final List componentAddress) { + this.componentManufacturer = componentManufacturer; + this.componentModel = componentModel; + this.componentSerial = componentSerial; + this.componentRevision = componentRevision; + this.componentManufacturerId = componentManufacturerId; + this.fieldReplaceable = fieldReplaceable; + this.componentAddress = componentAddress; + } + + /** + * Constructor given the SEQUENCE that contains Component Identifier. + * @param sequence containing the the component identifier + * @throws IllegalArgumentException if there was an error on the parsing + */ + public ComponentIdentifier(final ASN1Sequence sequence) throws IllegalArgumentException { + // set all optional values to default in case they aren't set. + this(); + //Check if it have a valid number of identifiers + if (sequence.size() < MANDATORY_ELEMENTS) { + throw new IllegalArgumentException("Component identifier do not have required values."); + } + + //Mandatory values + componentManufacturer = DERUTF8String.getInstance(sequence.getObjectAt(0)); + componentModel = DERUTF8String.getInstance(sequence.getObjectAt(1)); + + //Continue reading the sequence if it does contain more than 2 values + for (int i = 2; i < sequence.size(); i++) { + ASN1TaggedObject taggedObj = ASN1TaggedObject.getInstance(sequence.getObjectAt(i)); + switch (taggedObj.getTagNo()) { + case COMPONENT_SERIAL: + componentSerial = DERUTF8String.getInstance(taggedObj, false); + break; + case COMPONENT_REVISION: + componentRevision = DERUTF8String.getInstance(taggedObj, false); + break; + case COMPONENT_MANUFACTURER_ID: + componentManufacturerId = ASN1ObjectIdentifier.getInstance(taggedObj, false); + break; + case FIELD_REPLACEABLE: + fieldReplaceable = ASN1Boolean.getInstance(taggedObj, false); + break; + case COMPONENT_ADDRESS: + ASN1Sequence addressesSequence = ASN1Sequence.getInstance(taggedObj, false); + componentAddress = retrieveComponentAddress(addressesSequence); + break; + default: + throw new IllegalArgumentException("Component identifier contains " + + "invalid tagged object."); + } + } + } + + /** + * Get all the component addresses inside the sequence. + * + * @param sequence that contains the component addresses. + * @return list of component addresses inside the sequence + * @throws IllegalArgumentException if there was an error on the parsing + */ + public static List retrieveComponentAddress(final ASN1Sequence sequence) + throws IllegalArgumentException { + List addresses; + addresses = new ArrayList<>(); + + if (sequence.size() > CONFIGMAX) { + throw new IllegalArgumentException("Component identifier contains invalid number " + + "of component addresses."); + } + //Get the components + for (int i = 0; i < sequence.size(); i++) { + ASN1Sequence address = ASN1Sequence.getInstance(sequence.getObjectAt(i)); + addresses.add(new ComponentAddress(address)); + } + + return Collections.unmodifiableList(addresses); + } + + /** + * @return indicates the type of platform certificate + */ + public boolean isVersion2() { + return false; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("ComponentIdentifier{"); + sb.append("componentManufacturer=").append(componentManufacturer.getString()); + sb.append(", componentModel=").append(componentModel.getString()); + //Optional not null values + sb.append(", componentSerial="); + if (componentSerial != null) { + sb.append(componentSerial.getString()); + } + sb.append(", componentRevision="); + if (componentRevision != null) { + sb.append(componentRevision.getString()); + } + sb.append(", componentManufacturerId="); + if (componentManufacturerId != null) { + sb.append(componentManufacturerId.getId()); + } + sb.append(", fieldReplaceable="); + if (fieldReplaceable != null) { + sb.append(fieldReplaceable.toString()); + } + sb.append(", componentAddress="); + if (componentAddress.size() > 0) { + sb.append(componentAddress + .stream() + .map(Object::toString) + .collect(Collectors.joining(","))); + } + sb.append(", certificateIdentifier="); + sb.append("}"); + + return sb.toString(); + } +} diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/attributes/FIPSLevel.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/attributes/FIPSLevel.java new file mode 100644 index 00000000..646ecfe9 --- /dev/null +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/attributes/FIPSLevel.java @@ -0,0 +1,122 @@ +package hirs.attestationca.persist.entity.userdefined.certificate.attributes; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.Setter; +import org.bouncycastle.asn1.ASN1Boolean; +import org.bouncycastle.asn1.ASN1Enumerated; +import org.bouncycastle.asn1.ASN1Sequence; +import org.bouncycastle.asn1.DERIA5String; + +/** + * Basic class that handle FIPS Level. + *
+ * FIPSLevel ::= SEQUENCE {
+ *      version IA5STRING (SIZE (1..STRMAX)), -- "140-1" or "140-2"
+ *      level SecurityLevel,
+ *      plus BOOLEAN DEFAULT FALSE }
+ * 
+ */ +@AllArgsConstructor +public class FIPSLevel { + + private static final int MAX_SEQUENCE_SIZE = 3; + /** + * A type to handle the security Level used in the FIPS Level. + * Ordering of enum types is intentional and their ordinal values correspond to enum + * values in the TCG spec. + * + *
+     * SecurityLevel ::= ENUMERATED {
+     *      level1 (1),
+     *      level2 (2),
+     *      level3 (3),
+     *      level4 (4) }
+     * 
+ */ + public enum SecurityLevel { + /** + * Security Level 1. + */ + LEVEL1("level 1"), + /** + * Security Level 2. + */ + LEVEL2("level 2"), + /** + * Security Level 3. + */ + LEVEL3("level 3"), + /** + * Security Level 4. + */ + LEVEL4("level 4"); + + private final String value; + /** + * Basic constructor. + * @param value string containing the value. + */ + SecurityLevel(final String value) { + this.value = value; + } + + /** + * Get the string value from the StrengthOfFunction. + * @return the string containing the value. + */ + public String getValue() { + return this.value; + } + } + + @Getter @Setter + private DERIA5String version; + @Getter @Setter + private SecurityLevel level; + @Getter @Setter + private ASN1Boolean plus; + + /** + * Default constructor. + */ + public FIPSLevel() { + version = null; + level = null; + plus = null; + } + + /** + * Constructor given the SEQUENCE that contains the FIPLevel Object. + * + * @param sequence containing the FIPS Level Object + * @throws IllegalArgumentException if there was an error on the parsing + */ + public FIPSLevel(final ASN1Sequence sequence) throws IllegalArgumentException { + //Get version + version = DERIA5String.getInstance(sequence.getObjectAt(0)); + //Get and validate level + ASN1Enumerated enumarated = ASN1Enumerated.getInstance(sequence.getObjectAt(1)); + //Throw exception when is not between 1 and 7 + if (enumarated.getValue().intValue() <= 0 + || enumarated.getValue().intValue() > SecurityLevel.values().length) { + throw new IllegalArgumentException("Invalid security level on FIPSLevel."); + } + level = SecurityLevel.values()[enumarated.getValue().intValue() - 1]; + + //Check if there is another value on the sequence for the plus + plus = ASN1Boolean.FALSE; //Default to false + if (sequence.size() == MAX_SEQUENCE_SIZE) { + plus = ASN1Boolean.getInstance(sequence.getObjectAt(2)); + } + } + + @Override + public String toString() { + return "FIPSLevel{" + + "version=" + version.getString() + + ", level=" + level.getValue() + + ", plus=" + plus.toString() + + '}'; + } +} diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/attributes/PlatformConfiguration.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/attributes/PlatformConfiguration.java new file mode 100644 index 00000000..18ce3213 --- /dev/null +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/attributes/PlatformConfiguration.java @@ -0,0 +1,104 @@ +package hirs.attestationca.persist.entity.userdefined.certificate.attributes; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.Setter; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Abstract class that provides base info for Platform Configuration of + * the Platform Certificate Attribute. + */ +@AllArgsConstructor +public abstract class PlatformConfiguration { + private List componentIdentifier; + @Getter @Setter + private URIReference componentIdentifierUri; + private List platformProperties; + @Getter @Setter + private URIReference platformPropertiesUri; + + /** + * Default constructor. + */ + public PlatformConfiguration() { + this.componentIdentifier = new ArrayList<>(); + this.componentIdentifierUri = null; + this.platformProperties = new ArrayList<>(); + this.platformPropertiesUri = null; + } + + /** + * Constructor given the Platform Configuration values. + * + * @param componentIdentifier list containing all the components inside the + * Platform Configuration. + * @param platformProperties list containing all the properties inside the + * Platform Configuration. + * @param platformPropertiesUri object containing the URI Reference + */ + public PlatformConfiguration(final List componentIdentifier, + final List platformProperties, + final URIReference platformPropertiesUri) { + this.componentIdentifier = componentIdentifier; + this.platformProperties = platformProperties; + this.platformPropertiesUri = platformPropertiesUri; + } + + /** + * @return the componentIdentifier + */ + public List getComponentIdentifier() { + return Collections.unmodifiableList(componentIdentifier); + } + + /** + * Add function for the component identifier array. + * @param componentIdentifier object to add + * @return status of the add, if successful or not + */ + protected boolean add(final ComponentIdentifier componentIdentifier) { + if (this.componentIdentifier != null) { + return this.componentIdentifier.add(componentIdentifier); + } + + return false; + } + + /** + * @param componentIdentifier the componentIdentifier to set + */ + public void setComponentIdentifier(final List componentIdentifier) { + this.componentIdentifier = componentIdentifier; + } + + /** + * @return the platformProperties + */ + public List getPlatformProperties() { + return Collections.unmodifiableList(platformProperties); + } + + /** + * Add function for the platform property array. + * @param platformProperty property object to add + * @return status of the add, if successful or not + */ + protected boolean add(final PlatformProperty platformProperty) { + if (this.platformProperties != null) { + return this.platformProperties.add(platformProperty); + } + + return false; + } + + /** + * @param platformProperties the platformProperties to set + */ + public void setPlatformProperties(final List platformProperties) { + this.platformProperties = platformProperties; + } +} diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/attributes/PlatformConfigurationV1.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/attributes/PlatformConfigurationV1.java new file mode 100644 index 00000000..215aef36 --- /dev/null +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/attributes/PlatformConfigurationV1.java @@ -0,0 +1,105 @@ +package hirs.attestationca.persist.entity.userdefined.certificate.attributes; + +import org.bouncycastle.asn1.ASN1Sequence; +import org.bouncycastle.asn1.ASN1TaggedObject; + +import java.util.ArrayList; +import java.util.stream.Collectors; + +/** + * Basic class that handle Platform Configuration for the Platform Certificate + * Attribute. + *
+ * PlatformConfiguration ::= SEQUENCE {
+ *      componentIdentifier [0] IMPLICIT SEQUENCE(SIZE(1..CONFIGMAX)) OF
+ *           ComponentIdentifier OPTIONAL,
+ *      platformProperties [1] IMPLICIT SEQUENCE(SIZE(1..CONFIGMAX)) OF Properties OPTIONAL,
+ *      platformPropertiesUri [2] IMPLICIT URIReference OPTIONAL }
+ * 
+ */ +public class PlatformConfigurationV1 extends PlatformConfiguration { + + private static final int COMPONENT_IDENTIFIER = 0; + private static final int PLATFORM_PROPERTIES = 1; + private static final int PLATFORM_PROPERTIES_URI = 2; + + /** + * Constructor given the SEQUENCE that contains Platform Configuration. + * @param sequence containing the the Platform Configuration. + * @throws IllegalArgumentException if there was an error on the parsing + */ + public PlatformConfigurationV1(final ASN1Sequence sequence) throws IllegalArgumentException { + + //Default values + setComponentIdentifier(new ArrayList<>()); + setPlatformProperties(new ArrayList<>()); + setPlatformPropertiesUri(null); + + for (int i = 0; i < sequence.size(); i++) { + ASN1TaggedObject taggedSequence + = ASN1TaggedObject.getInstance(sequence.getObjectAt(i)); + //Set information based on the set tagged + switch (taggedSequence.getTagNo()) { + case COMPONENT_IDENTIFIER: + //Get componentIdentifier + ASN1Sequence componentConfiguration + = ASN1Sequence.getInstance(taggedSequence, false); + + //Get and set all the component values + for (int j = 0; j < componentConfiguration.size(); j++) { + //DERSequence with the components + ASN1Sequence component + = ASN1Sequence.getInstance(componentConfiguration.getObjectAt(j)); + add(new ComponentIdentifier(component)); + } + break; + case PLATFORM_PROPERTIES: + //Get platformProperties + ASN1Sequence properties = ASN1Sequence.getInstance(taggedSequence, false); + + //Get and set all the properties values + for (int j = 0; j < properties.size(); j++) { + //DERSequence with the components + ASN1Sequence property = ASN1Sequence.getInstance(properties.getObjectAt(j)); + add(new PlatformProperty(property)); + } + break; + case PLATFORM_PROPERTIES_URI: + //Get platformPropertiesURI + ASN1Sequence propertiesUri = ASN1Sequence.getInstance(taggedSequence, false); + //Save properties URI + setPlatformPropertiesUri(new URIReference(propertiesUri)); + break; + default: + break; + } + } + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("PlatformConfiguration{"); + sb.append("componentIdentifier="); + if (getComponentIdentifier().size() > 0) { + sb.append(getComponentIdentifier() + .stream() + .map(Object::toString) + .collect(Collectors.joining(","))); + } + sb.append(", platformProperties="); + if (getPlatformProperties().size() > 0) { + sb.append(getPlatformProperties() + .stream() + .map(Object::toString) + .collect(Collectors.joining(","))); + } + sb.append(", platformPropertiesUri="); + if (getPlatformPropertiesUri() != null) { + sb.append(getPlatformPropertiesUri().toString()); + } + sb.append("}"); + + return sb.toString(); + } +} diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/attributes/PlatformProperty.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/attributes/PlatformProperty.java new file mode 100644 index 00000000..afe68d0d --- /dev/null +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/attributes/PlatformProperty.java @@ -0,0 +1,67 @@ +package hirs.attestationca.persist.entity.userdefined.certificate.attributes; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.Setter; +import org.bouncycastle.asn1.ASN1Sequence; +import org.bouncycastle.asn1.DERUTF8String; + +/** + * + * Basic class that handles a single property for the platform configuration. + *
+ * Properties ::= SEQUENCE {
+ *      propertyName UTF8String (SIZE (1..STRMAX)),
+ *      propertyValue UTF8String (SIZE (1..STRMAX) }
+ *
+ * 
+ */ +@Getter +@Setter +@AllArgsConstructor +public class PlatformProperty { + + private static final String NOT_SPECIFIED = "Not Specified"; + + /** + * Number of identifiers for version 1. + */ + protected static final int IDENTIFIER_NUMBER = 2; + + private DERUTF8String propertyName; + private DERUTF8String propertyValue; + + /** + * Default constructor. + */ + public PlatformProperty() { + this.propertyName = new DERUTF8String(NOT_SPECIFIED); + this.propertyValue = new DERUTF8String(NOT_SPECIFIED); + } + + /** + * Constructor given the SEQUENCE that contains the name and value for the + * platform property. + * + * @param sequence containing the name and value of the platform property + * @throws IllegalArgumentException if there was an error on the parsing + */ + public PlatformProperty(final ASN1Sequence sequence) throws IllegalArgumentException { + // Check if the sequence contains the two values required + if (sequence.size() != IDENTIFIER_NUMBER) { + throw new IllegalArgumentException("Platform properties does not contain all " + + "the required fields."); + } + + this.propertyName = DERUTF8String.getInstance(sequence.getObjectAt(0)); + this.propertyValue = DERUTF8String.getInstance(sequence.getObjectAt(1)); + } + + @Override + public String toString() { + return "PlatformProperty{" + + "propertyName=" + propertyName.getString() + + ", propertyValue=" + propertyValue.getString() + + "}"; + } +} diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/attributes/TBBSecurityAssertion.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/attributes/TBBSecurityAssertion.java new file mode 100644 index 00000000..618db8e1 --- /dev/null +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/attributes/TBBSecurityAssertion.java @@ -0,0 +1,282 @@ +package hirs.attestationca.persist.entity.userdefined.certificate.attributes; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.bouncycastle.asn1.ASN1Boolean; +import org.bouncycastle.asn1.ASN1Enumerated; +import org.bouncycastle.asn1.ASN1Integer; +import org.bouncycastle.asn1.ASN1Sequence; +import org.bouncycastle.asn1.ASN1TaggedObject; +import org.bouncycastle.asn1.DERIA5String; + +import java.math.BigInteger; + +/** + * Basic class that handle component identifiers from the Platform Configuration + * Attribute. + *
+ * TBBSecurityAssertions ::= SEQUENCE {
+ *      version Version DEFAULT v1,
+ *      ccInfo [0] IMPLICIT CommonCriteriaMeasures OPTIONAL,
+ *      fipsLevel [1] IMPLICIT FIPSLevel OPTIONAL,
+ *      rtmType [2] IMPLICIT MeasurementRootType OPTIONAL,
+ *      iso9000Certified BOOLEAN DEFAULT FALSE,
+ *      iso9000Uri IA5STRING (SIZE (1..URIMAX)) OPTIONAL }
+ * 
+ */ +@AllArgsConstructor +public class TBBSecurityAssertion { + + private static final int CCINFO = 0; + private static final int FIPSLEVEL = 1; + private static final int RTMTYPE = 2; + + /** + * A type to handle the evaluation status used in the Common Criteria Measurement. + * Ordering of enum types is intentional and their ordinal values correspond to enum + * values in the TCG spec. + * + *
+     * MeasurementRootType ::= ENUMERATED {
+     *    static (0),
+     *    dynamic (1),
+     *    nonHost (2),
+     *    hybrid (3),
+     *    physical (4),
+     *    virtual (5) }
+     * 
+ */ + public enum MeasurementRootType { + /** + * Static measurement root type. + */ + STATIC("static"), + /** + * Dynamic measurement root type. + */ + DYNAMIC("dynamic"), + /** + * Non-Host measurement root type. + */ + NONHOST("nonHost"), + /** + * Hybrid measurement root type. + */ + HYBRID("hybrid"), + /** + * Physical measurement root type. + */ + PHYSICAL("physical"), + /** + * Virtual measurement root type. + */ + VIRTUAL("virtual"); + + @Getter + private final String value; + + /** + * Basic constructor. + * @param value string containing the value. + */ + MeasurementRootType(final String value) { + this.value = value; + } + } + + private ASN1Integer version; + private CommonCriteriaMeasures ccInfo; + private FIPSLevel fipsLevel; + private MeasurementRootType rtmType; + private ASN1Boolean iso9000Certified; + private DERIA5String iso9000Uri; + + /** + * Default constructor. + */ + public TBBSecurityAssertion() { + version = null; + ccInfo = null; + fipsLevel = null; + rtmType = null; + iso9000Certified = null; + iso9000Uri = null; + } + + /** + * Constructor given the SEQUENCE that contains a TBBSecurityAssertion Object. + * @param sequence containing the the TBB Security Assertion + * @throws IllegalArgumentException if there was an error on the parsing + */ + public TBBSecurityAssertion(final ASN1Sequence sequence) throws IllegalArgumentException { + int index = 0; + //sequence size + int sequenceSize = sequence.size(); + + //Default values + version = new ASN1Integer(BigInteger.valueOf(0)); //Default v1 (0) + ccInfo = null; + fipsLevel = null; + rtmType = null; + iso9000Certified = ASN1Boolean.FALSE; + iso9000Uri = null; + + // Only contains defaults + if (sequence.size() == 0) { + return; + } + + // Get version if present + if (sequence.getObjectAt(index).toASN1Primitive() instanceof ASN1Integer) { + version = ASN1Integer.getInstance(sequence.getObjectAt(index)); + index++; + } + + // Check if it's a tag value + while (index < sequenceSize + && sequence.getObjectAt(index).toASN1Primitive() instanceof ASN1TaggedObject) { + ASN1TaggedObject taggedObj = ASN1TaggedObject.getInstance(sequence.getObjectAt(index)); + switch (taggedObj.getTagNo()) { + case CCINFO: + ASN1Sequence cciSequence = ASN1Sequence.getInstance(taggedObj, false); + ccInfo = new CommonCriteriaMeasures(cciSequence); + break; + case FIPSLEVEL: + ASN1Sequence fipsSequence = ASN1Sequence.getInstance(taggedObj, false); + fipsLevel = new FIPSLevel(fipsSequence); + break; + case RTMTYPE: + ASN1Enumerated enumerated = ASN1Enumerated.getInstance(taggedObj, false); + rtmType = MeasurementRootType.values()[enumerated.getValue().intValue()]; + break; + default: + throw new IllegalArgumentException("TBB Security Assertion contains " + + "invalid tagged object."); + } + index++; + } + // Check if it's a boolean + if (index < sequenceSize + && sequence.getObjectAt(index).toASN1Primitive() instanceof ASN1Boolean) { + iso9000Certified = ASN1Boolean.getInstance(sequence.getObjectAt(index)); + index++; + } + // Check if it's a IA5String + if (index < sequenceSize + && sequence.getObjectAt(index).toASN1Primitive() instanceof DERIA5String) { + iso9000Uri = DERIA5String.getInstance(sequence.getObjectAt(index)); + } + } + + /** + * @return the version + */ + public ASN1Integer getVersion() { + return version; + } + + /** + * @param version the version to set + */ + public void setVersion(final ASN1Integer version) { + this.version = version; + } + + /** + * @return the ccInfo + */ + public CommonCriteriaMeasures getCcInfo() { + return ccInfo; + } + + /** + * @param ccInfo the ccInfo to set + */ + public void setCcInfo(final CommonCriteriaMeasures ccInfo) { + this.ccInfo = ccInfo; + } + + /** + * @return the fipsLevel + */ + public FIPSLevel getFipsLevel() { + return fipsLevel; + } + + /** + * @param fipsLevel the fipsLevel to set + */ + public void setFipsLevel(final FIPSLevel fipsLevel) { + this.fipsLevel = fipsLevel; + } + + /** + * @return the rtmType + */ + public MeasurementRootType getRtmType() { + return rtmType; + } + + /** + * @param rtmType the rtmType to set + */ + public void setRtmType(final MeasurementRootType rtmType) { + this.rtmType = rtmType; + } + + /** + * @return the iso9000Certified + */ + public ASN1Boolean getIso9000Certified() { + return iso9000Certified; + } + + /** + * @param iso9000Certified the iso9000Certified to set + */ + public void setIso9000Certified(final ASN1Boolean iso9000Certified) { + this.iso9000Certified = iso9000Certified; + } + + /** + * @return the iso9000Uri + */ + public DERIA5String getIso9000Uri() { + return iso9000Uri; + } + + /** + * @param iso9000Uri the iso9000Uri to set + */ + public void setIso9000Uri(final DERIA5String iso9000Uri) { + this.iso9000Uri = iso9000Uri; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("TBBSecurityAssertion{"); + sb.append("version=").append(version.toString()); + //Optional values not null + sb.append(", ccInfo="); + if (ccInfo != null) { + sb.append(ccInfo.toString()); + } + sb.append(", fipsLevel="); + if (fipsLevel != null) { + sb.append(fipsLevel.toString()); + } + sb.append(", rtmType="); + if (rtmType != null) { + sb.append(rtmType.getValue()); + } + sb.append(", iso9000Certified=").append(iso9000Certified.toString()); + sb.append(", iso9000Uri="); + if (iso9000Uri != null) { + sb.append(iso9000Uri.getString()); + } + sb.append("}"); + + return sb.toString(); + } +} diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/attributes/TPMSecurityAssertions.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/attributes/TPMSecurityAssertions.java new file mode 100644 index 00000000..446d33e4 --- /dev/null +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/attributes/TPMSecurityAssertions.java @@ -0,0 +1,121 @@ +package hirs.attestationca.persist.entity.userdefined.certificate.attributes; + +import jakarta.persistence.Column; +import jakarta.persistence.Embeddable; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.math.BigInteger; + +/** + * A class to represent the TPM Security Assertions in an Endorsement Credential as + * defined by the TCG spec for TPM 1.2. + * + * https://www.trustedcomputinggroup.org/wp-content/uploads/IWG-Credential_Profiles_V1_R0.pdf + * + * Future iterations of this code may want to reference + * www.trustedcomputinggroup.org/wp-content/uploads/Credential_Profile_EK_V2.0_R14_published.pdf + * for specifications for TPM 2.0 (pg. 19). + */ +@AllArgsConstructor +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@Getter @Setter +@Embeddable +public class TPMSecurityAssertions { + + /** + * A type to handle the different endorsement key generation types used in the TPM + * Assertions field of an endorsement credential. Ordering of enum types is intentional + * and their ordinal values correspond to enum values in the TCG spec. + */ + + public enum EkGenerationType { + /** + * Generated internally within the TPM and cannot be revoked. Enum value of 0. + */ + INTERNAL, + /** + * Generated externally and then inserted under a controlled environment during + * manufacturing. Cannot be revoked. Enum value of 1. + */ + INJECTED, + /** + * Generated internally within the TPM and can be revoked. Enum value of 2. + */ + INTERNAL_REVOCABLE, + /** + * Generated externally and then inserted under a controlled environment during + * manufacturing. Can be revoked. Enum value of 3. + */ + INJECTED_REVOCABLE; + } + + /** + * A type to handle the different endorsement key generation locations used in + * specifying the endorsement key generation location and the endorsement key + * certificate generation location in the TPM Assertions field of an endorsement + * credential. Ordering of enum types is intentional and their ordinal values + * correspond to enum values in the TCG spec. + */ + public enum EkGenerationLocation { + /** + * Generated by the TPM Manufacturer. Enum value of 0. + */ + TPM_MANUFACTURER, + /** + * Generated by the Platform Manufacturer. Enum value of 1. + */ + PLATFORM_MANUFACTURER, + /** + * Generated by the endorsement key certificate signer. Enum value of 2. + */ + EK_CERT_SIGNER; + } + + @Column + private BigInteger tpmSecAssertsVersion; //default v1 + + @Column + private boolean fieldUpgradeable; //default false + + @Column(nullable = true) + private EkGenerationType ekGenType; //optional + + @Column(nullable = true) + private EkGenerationLocation ekGenerationLocation; //optional + + @Column(nullable = true) + private EkGenerationLocation ekCertificateGenerationLocation; //optional + + // Future work (may need to create other classes): + //private CommonCriteriaMeasures commCritMeasures; //optional + //private FIPSLevel fipsLevel; //optional + //private boolean iso9000Certified; //default false + //private IA5String iso9000Uri; //optional + + /** + * Standard constructor that sets required fields. Use accessor methods + * to set optional fields. + * @param version the version of the security assertions + * @param fieldUpgradeable whether or not the security assertions are + * field upgradeable. + */ + public TPMSecurityAssertions(final BigInteger version, final boolean fieldUpgradeable) { + this.tpmSecAssertsVersion = version; + this.fieldUpgradeable = fieldUpgradeable; + } + + @Override + public String toString() { + return "TPMSecurityAssertions{" + + "version=" + tpmSecAssertsVersion + + ", fieldUpgradeable=" + fieldUpgradeable + + ", ekGenType=" + ekGenType + + ", ekGenLoc=" + ekGenerationLocation + + ", ekCertGenLoc=" + ekCertificateGenerationLocation + + '}'; + } +} diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/attributes/TPMSpecification.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/attributes/TPMSpecification.java new file mode 100644 index 00000000..ad828aca --- /dev/null +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/attributes/TPMSpecification.java @@ -0,0 +1,58 @@ +package hirs.attestationca.persist.entity.userdefined.certificate.attributes; + +import jakarta.persistence.Column; +import jakarta.persistence.Embeddable; +import lombok.AccessLevel; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import java.math.BigInteger; + +/** + * A class to represent the TPM Specification in an Endorsement Credential as + * defined by the TCG spec for TPM 1.2. + * + * https://www.trustedcomputinggroup.org/wp-content/uploads/IWG-Credential_Profiles_V1_R0.pdf + * + * Future iterations of this code may want to reference + * www.trustedcomputinggroup.org/wp-content/uploads/Credential_Profile_EK_V2.0_R14_published.pdf + * for specifications for TPM 2.0. + */ +@EqualsAndHashCode +@NoArgsConstructor(access= AccessLevel.PROTECTED) +@Getter +@Embeddable +public class TPMSpecification { + + @Column + private String family; + + @Column + private BigInteger level; + + @Column + private BigInteger revision; + + /** + * Standard constructor. + * @param family the specification family. + * @param level the specification level. + * @param revision the specification revision. + */ + public TPMSpecification(final String family, final BigInteger level, + final BigInteger revision) { + this.family = family; + this.level = level; + this.revision = revision; + } + + @Override + public String toString() { + return "TPMSpecification{" + + "family='" + family + '\'' + + ", level=" + level + + ", revision=" + revision + + '}'; + } +} diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/attributes/URIReference.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/attributes/URIReference.java new file mode 100644 index 00000000..2397cd2e --- /dev/null +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/attributes/URIReference.java @@ -0,0 +1,91 @@ +package hirs.attestationca.persist.entity.userdefined.certificate.attributes; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.Setter; +import org.bouncycastle.asn1.ASN1Sequence; +import org.bouncycastle.asn1.DERBitString; +import org.bouncycastle.asn1.DERIA5String; +import org.bouncycastle.asn1.x509.AlgorithmIdentifier; + +/** + * + * Basic class that handle a URIReference object. + *
+ * URIReference ::= SEQUENCE {
+ *      uniformResourceIdentifier IA5String (SIZE (1..URIMAX)),
+ *      hashAlgorithm AlgorithmIdentifier OPTIONAL,
+ *      hashValue BIT STRING OPTIONAL
+ }
+ * 
+ */ +@Getter @Setter +@AllArgsConstructor +public class URIReference { + private DERIA5String uniformResourceIdentifier; + private AlgorithmIdentifier hashAlgorithm; + @JsonIgnore + private DERBitString hashValue; + + private static final int PLATFORM_PROPERTIES_URI_MAX = 3; + private static final int PLATFORM_PROPERTIES_URI_MIN = 1; + + /** + * Default constructor. + */ + public URIReference() { + this.uniformResourceIdentifier = null; + this.hashAlgorithm = null; + this.hashValue = null; + } + + /** + * Constructor given the SEQUENCE that contains the URIReference values. + * + * @param sequence containing the name and value of the platform property + * @throws IllegalArgumentException if there was an error on the parsing + */ + public URIReference(final ASN1Sequence sequence) throws IllegalArgumentException { + //Check if the sequence contains the two values required + if (sequence.size() > PLATFORM_PROPERTIES_URI_MAX + || sequence.size() < PLATFORM_PROPERTIES_URI_MIN) { + throw new IllegalArgumentException("PlatformPropertiesURI contains invalid " + + "number of fields."); + } + + //Get the Platform Configuration URI values + for (int j = 0; j < sequence.size(); j++) { + if (sequence.getObjectAt(j) instanceof DERIA5String) { + this.uniformResourceIdentifier = DERIA5String.getInstance(sequence.getObjectAt(j)); + } else if ((sequence.getObjectAt(j) instanceof AlgorithmIdentifier) + || (sequence.getObjectAt(j) instanceof ASN1Sequence)) { + this.hashAlgorithm = + AlgorithmIdentifier.getInstance(sequence.getObjectAt(j)); + } else if (sequence.getObjectAt(j) instanceof DERBitString) { + this.hashValue = DERBitString.getInstance(sequence.getObjectAt(j)); + } else { + throw new IllegalArgumentException("Unexpected DER type found. " + + sequence.getObjectAt(j).getClass().getName() + " found at index " + j + "."); + } + } + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("URIReference{"); + sb.append("uniformResourceIdentifier=").append(uniformResourceIdentifier.getString()); + //Check of optional values are not null + sb.append(", hashAlgorithm="); + if (hashAlgorithm != null) { + sb.append(hashAlgorithm.getAlgorithm().getId()); + } + sb.append(", hashValue="); + if (hashValue != null) { + sb.append(hashValue.getString()); + } + sb.append("}"); + return sb.toString(); + } +} diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/attributes/V2/AttributeStatus.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/attributes/V2/AttributeStatus.java new file mode 100644 index 00000000..5a5b9b03 --- /dev/null +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/attributes/V2/AttributeStatus.java @@ -0,0 +1,40 @@ +package hirs.attestationca.persist.entity.userdefined.certificate.attributes.V2; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.apache.commons.lang3.StringUtils; + +/** + * A type to handle the security Level used in the FIPS Level. + * Ordering of enum types is intentional and their ordinal values correspond to enum + * values in the TCG spec. + * + *
+ * AttributeStatus ::= ENUMERATED {
+ *      added (0),
+ *      modified (1),
+ *      removed (2) }
+ * 
+ */ +@AllArgsConstructor +public enum AttributeStatus { + /** + * Attribute Status for ADDED. + */ + ADDED("added"), + /** + * Attribute Status for MODIFIED. + */ + MODIFIED("modified"), + /** + * Attribute Status for REMOVED. + */ + REMOVED("removed"), + /** + * Attribute Status for EMPTY. + */ + EMPTY_STATUS(StringUtils.EMPTY); + + @Getter + private final String value; +} diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/attributes/V2/CertificateIdentifier.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/attributes/V2/CertificateIdentifier.java new file mode 100644 index 00000000..767bd045 --- /dev/null +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/attributes/V2/CertificateIdentifier.java @@ -0,0 +1,127 @@ +package hirs.attestationca.persist.entity.userdefined.certificate.attributes.V2; + +import lombok.Getter; +import org.bouncycastle.asn1.ASN1Integer; +import org.bouncycastle.asn1.ASN1Sequence; +import org.bouncycastle.asn1.ASN1TaggedObject; +import org.bouncycastle.asn1.DERSequence; +import org.bouncycastle.asn1.x509.GeneralName; + +import java.math.BigInteger; + +/** + * Basic class that handles a the attribute associate with a Certificate + * Identifier for the component. + *
+ * CertificateIdentifier::= SEQUENCE {
+ *       attributeCertIdentifier [0] IMPLICIT AttributeCertificateIdentifier OPTIONAL
+ *       genericCertIdentifier   [1] IMPLICIT IssuerSerial OPTIONAL }
+ *
+ * AttributeCertificateIdentifier ::= SEQUENCE {
+ *       hashAlgorithm  AlgorithmIdentifier,
+ *       hashOverSignatureValue OCTET STRING }
+ *
+ * IssuerSerial ::= SEQUENCE {
+ *       issuer        GeneralNames,
+ *       serial        CertificateSerialNumber }
+ * 
+ */ +@Getter +public class CertificateIdentifier { + private static final String NOT_SPECIFIED = "Not Specified"; + + private static final int SEQUENCE_NUMBER = 2; + private static final int ATTRIBUTE_ID_INDEX = 0; + private static final int GENERIC_ID_INDEX = 1; + + private String hashAlgorithm; + private String hashSigValue; + private GeneralName issuerDN; + private BigInteger certificateSerialNumber; + + /** + * Default constructor. + */ + public CertificateIdentifier() { + hashAlgorithm = NOT_SPECIFIED; + hashSigValue = null; + issuerDN = null; + certificateSerialNumber = BigInteger.ZERO; + } + + /** + * Primary constructor for the parsing of the sequence. + * @param sequence containing the name and value of the Certificate Identifier + */ + public CertificateIdentifier(final ASN1Sequence sequence) { + this(); + + ASN1TaggedObject taggedObj; + for (int i = 0; i < sequence.size(); i++) { + taggedObj = ASN1TaggedObject.getInstance(sequence.getObjectAt(i)); + + switch (taggedObj.getTagNo()) { + case ATTRIBUTE_ID_INDEX: + // attributecertificateidentifier + parseAttributeCertId(ASN1Sequence.getInstance(taggedObj, false)); + break; + case GENERIC_ID_INDEX: + // issuerserial + parseGenericCertId(ASN1Sequence.getInstance(taggedObj, false)); + break; + default: + break; + } + } + } + + private void parseAttributeCertId(final ASN1Sequence attrCertSeq) { + //Check if it have a valid number of identifiers + if (attrCertSeq.size() != SEQUENCE_NUMBER) { + throw new IllegalArgumentException("CertificateIdentifier" + + ".AttributeCertificateIdentifier does not have required values."); + } + + hashAlgorithm = attrCertSeq.getObjectAt(0).toString(); + hashSigValue = attrCertSeq.getObjectAt(1).toString(); + } + + private void parseGenericCertId(final ASN1Sequence issuerSerialSeq) { + //Check if it have a valid number of identifiers + if (issuerSerialSeq.size() != SEQUENCE_NUMBER) { + throw new IllegalArgumentException("CertificateIdentifier" + + ".GenericCertificateIdentifier does not have required values."); + } + + ASN1Sequence derSequence = DERSequence.getInstance(issuerSerialSeq.getObjectAt(0)); + ASN1TaggedObject taggedObj = ASN1TaggedObject.getInstance(derSequence.getObjectAt(0)); + + issuerDN = GeneralName.getInstance(taggedObj); + certificateSerialNumber = ASN1Integer.getInstance(issuerSerialSeq + .getObjectAt(1)).getValue(); + } + + /** + * String for the internal data stored. + * @return String representation of the data. + */ + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + + sb.append("CertificateIdentifier{"); + sb.append("hashAlgorithm=").append(hashAlgorithm); + sb.append(", hashSigValue").append(hashSigValue); + sb.append(", issuerDN="); + if (issuerDN != null) { + sb.append(issuerDN.toString()); + } + sb.append(", certificateSerialNumber="); + if (certificateSerialNumber != null) { + sb.append(certificateSerialNumber.toString()); + } + + sb.append("}"); + return sb.toString(); + } +} diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/attributes/V2/ComponentIdentifierV2.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/attributes/V2/ComponentIdentifierV2.java new file mode 100644 index 00000000..a5e05fbb --- /dev/null +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/attributes/V2/ComponentIdentifierV2.java @@ -0,0 +1,251 @@ +package hirs.attestationca.persist.entity.userdefined.certificate.attributes.V2; + +import hirs.attestationca.persist.entity.userdefined.certificate.attributes.ComponentAddress; +import hirs.attestationca.persist.entity.userdefined.certificate.attributes.ComponentClass; +import hirs.attestationca.persist.entity.userdefined.certificate.attributes.ComponentIdentifier; +import hirs.attestationca.persist.entity.userdefined.certificate.attributes.URIReference; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; +import org.bouncycastle.asn1.ASN1Boolean; +import org.bouncycastle.asn1.ASN1Enumerated; +import org.bouncycastle.asn1.ASN1ObjectIdentifier; +import org.bouncycastle.asn1.ASN1Sequence; +import org.bouncycastle.asn1.ASN1TaggedObject; +import org.bouncycastle.asn1.DEROctetString; +import org.bouncycastle.asn1.DERUTF8String; + +import java.util.List; +import java.util.stream.Collectors; + +/** + * Basic class that handle component identifiers from the Platform Configuration + * Attribute. + *
+ * ComponentIdentifier ::= SEQUENCE {
+ *      componentManufacturer UTF8String (SIZE (1..STRMAX)),
+ *      componentModel UTF8String (SIZE (1..STRMAX)),
+ *      componentSerial[0] IMPLICIT UTF8String (SIZE (1..STRMAX)) OPTIONAL,
+ *      componentRevision [1] IMPLICIT UTF8String (SIZE (1..STRMAX)) OPTIONAL,
+ *      componentManufacturerId [2] IMPLICIT PrivateEnterpriseNumber OPTIONAL,
+ *      fieldReplaceable [3] IMPLICIT BOOLEAN OPTIONAL,
+ *      componentAddress [4] IMPLICIT
+ *          SEQUENCE(SIZE(1..CONFIGMAX)) OF ComponentAddress OPTIONAL
+ *      componentPlatformCert [5] IMPLICIT CertificateIdentifier OPTIONAL,
+ *      componentPlatformCertUri [6] IMPLICIT URIReference OPTIONAL,
+ *      status [7] IMPLICIT AttributeStatus OPTIONAL }
+ * where STRMAX is 256, CONFIGMAX is 32
+ * 
+ */ +@Getter +@Setter +@EqualsAndHashCode(callSuper = false) +public class ComponentIdentifierV2 extends ComponentIdentifier { + + private static final int MANDATORY_ELEMENTS = 3; + // Additional optional identifiers for version 2 + private static final int COMPONENT_PLATFORM_CERT = 5; + private static final int COMPONENT_PLATFORM_URI = 6; + private static final int ATTRIBUTE_STATUS = 7; + + private ComponentClass componentClass; + private CertificateIdentifier certificateIdentifier; + private URIReference componentPlatformUri; + private AttributeStatus attributeStatus; + + /** + * Default constructor. + */ + public ComponentIdentifierV2() { + super(); + componentClass = new ComponentClass(); + certificateIdentifier = null; + componentPlatformUri = null; + attributeStatus = AttributeStatus.EMPTY_STATUS; + } + + /** + * Constructor given the components values. + * + * @param componentClass represent the component type + * @param componentManufacturer represents the component manufacturer + * @param componentModel represents the component model + * @param componentSerial represents the component serial number + * @param componentRevision represents the component revision + * @param componentManufacturerId represents the component manufacturer ID + * @param fieldReplaceable represents if the component is replaceable + * @param componentAddress represents a list of addresses + * @param certificateIdentifier object representing certificate Id + * @param componentPlatformUri object containing the URI Reference + * @param attributeStatus object containing enumerated status + */ + @SuppressWarnings("checkstyle:parameternumber") + public ComponentIdentifierV2(final ComponentClass componentClass, + final DERUTF8String componentManufacturer, + final DERUTF8String componentModel, + final DERUTF8String componentSerial, + final DERUTF8String componentRevision, + final ASN1ObjectIdentifier componentManufacturerId, + final ASN1Boolean fieldReplaceable, + final List componentAddress, + final CertificateIdentifier certificateIdentifier, + final URIReference componentPlatformUri, + final AttributeStatus attributeStatus) { + super(componentManufacturer, componentModel, componentSerial, + componentRevision, componentManufacturerId, fieldReplaceable, + componentAddress); + this.componentClass = componentClass; + // additional optional component identifiers + this.certificateIdentifier = certificateIdentifier; + this.componentPlatformUri = componentPlatformUri; + this.attributeStatus = attributeStatus; + } + + /** + * Constructor given the SEQUENCE that contains Component Identifier. + * @param sequence containing the the component identifier + * @throws IllegalArgumentException if there was an error on the parsing + */ + public ComponentIdentifierV2(final ASN1Sequence sequence) + throws IllegalArgumentException { + super(); + // Check if it have a valid number of identifiers + if (sequence.size() < MANDATORY_ELEMENTS) { + throw new IllegalArgumentException("Component identifier do not have required values."); + } + + int tag = 0; + ASN1Sequence componentIdSeq = ASN1Sequence.getInstance(sequence.getObjectAt(tag)); + componentClass = new ComponentClass(componentIdSeq.getObjectAt(tag++).toString(), + DEROctetString.getInstance(componentIdSeq.getObjectAt(tag)).toString()); + + // Mandatory values + this.setComponentManufacturer(DERUTF8String.getInstance(sequence.getObjectAt(tag++))); + this.setComponentModel(DERUTF8String.getInstance(sequence.getObjectAt(tag++))); + + // Continue reading the sequence if it does contain more than 2 values + for (int i = tag; i < sequence.size(); i++) { + ASN1TaggedObject taggedObj = ASN1TaggedObject.getInstance(sequence.getObjectAt(i)); + switch (taggedObj.getTagNo()) { + case COMPONENT_SERIAL: + this.setComponentSerial(DERUTF8String.getInstance(taggedObj, false)); + break; + case COMPONENT_REVISION: + this.setComponentRevision(DERUTF8String.getInstance(taggedObj, false)); + break; + case COMPONENT_MANUFACTURER_ID: + this.setComponentManufacturerId(ASN1ObjectIdentifier + .getInstance(taggedObj, false)); + break; + case FIELD_REPLACEABLE: + this.setFieldReplaceable(ASN1Boolean.getInstance(taggedObj, false)); + break; + case COMPONENT_ADDRESS: + ASN1Sequence addressesSequence = ASN1Sequence.getInstance(taggedObj, false); + this.setComponentAddress(retrieveComponentAddress(addressesSequence)); + break; + case COMPONENT_PLATFORM_CERT: + ASN1Sequence ciSequence = ASN1Sequence.getInstance(taggedObj, false); + certificateIdentifier = new CertificateIdentifier(ciSequence); + break; + case COMPONENT_PLATFORM_URI: + ASN1Sequence uriSequence = ASN1Sequence.getInstance(taggedObj, false); + this.componentPlatformUri = new URIReference(uriSequence); + break; + case ATTRIBUTE_STATUS: + ASN1Enumerated enumerated = ASN1Enumerated.getInstance(taggedObj, false); + this.attributeStatus = AttributeStatus.values()[ + enumerated.getValue().intValue()]; + break; + default: + throw new IllegalArgumentException("Component identifier contains " + + "invalid tagged object."); + } + } + } + + /** + * @return true if the component has been modified. + */ + public final boolean isAdded() { + return getAttributeStatus() == AttributeStatus.ADDED; + } + + /** + * @return true if the component has been modified. + */ + public final boolean isModified() { + return getAttributeStatus() == AttributeStatus.MODIFIED; + } + + /** + * @return true if the component has been removed. + */ + public final boolean isRemoved() { + return getAttributeStatus() == AttributeStatus.REMOVED; + } + + /** + * @return true if the component status wasn't set. + */ + public final boolean isEmpty() { + return (getAttributeStatus() == AttributeStatus.EMPTY_STATUS) + || (getAttributeStatus() == null); + } + + /** + * @return indicates the type of platform certificate. + */ + public boolean isVersion2() { + return true; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("ComponentIdentifierV2{"); + sb.append("componentClass=").append(componentClass); + sb.append(", componentManufacturer=").append(getComponentManufacturer() + .getString()); + sb.append(", componentModel=").append(getComponentModel().getString()); + // Optional not null values + sb.append(", componentSerial="); + if (getComponentSerial() != null) { + sb.append(getComponentSerial().getString()); + } + sb.append(", componentRevision="); + if (getComponentRevision() != null) { + sb.append(getComponentRevision().getString()); + } + sb.append(", componentManufacturerId="); + if (getComponentManufacturerId() != null) { + sb.append(getComponentManufacturerId().getId()); + } + sb.append(", fieldReplaceable="); + if (getFieldReplaceable() != null) { + sb.append(getFieldReplaceable().toString()); + } + sb.append(", componentAddress="); + if (getComponentAddress().size() > 0) { + sb.append(getComponentAddress() + .stream() + .map(Object::toString) + .collect(Collectors.joining(","))); + } + sb.append(", certificateIdentifier="); + if (certificateIdentifier != null) { + sb.append(certificateIdentifier.toString()); + } + sb.append(", componentPlatformUri="); + if (componentPlatformUri != null) { + sb.append(componentPlatformUri.toString()); + } + sb.append(", status="); + if (attributeStatus != null) { + sb.append(attributeStatus.getValue()); + } + sb.append("}"); + + return sb.toString(); + } +} diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/attributes/V2/PlatformConfigurationV2.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/attributes/V2/PlatformConfigurationV2.java new file mode 100644 index 00000000..c92019f0 --- /dev/null +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/attributes/V2/PlatformConfigurationV2.java @@ -0,0 +1,119 @@ +package hirs.attestationca.persist.entity.userdefined.certificate.attributes.V2; + +import hirs.attestationca.persist.entity.userdefined.certificate.attributes.PlatformConfiguration; +import hirs.attestationca.persist.entity.userdefined.certificate.attributes.URIReference; +import org.bouncycastle.asn1.ASN1Sequence; +import org.bouncycastle.asn1.ASN1TaggedObject; + +import java.util.ArrayList; +import java.util.stream.Collectors; + +/** + * Basic class that handle Platform Configuration for the Platform Certificate + * Attribute. + *
+ * PlatformConfiguration ::= SEQUENCE {
+ *      componentIdentifier [0] IMPLICIT SEQUENCE(SIZE(1..CONFIGMAX)) OF
+ *           ComponentIdentifier OPTIONAL,
+ *      componentIdentifiersUri [1] IMPLICIT URIReference OPTIONAL
+ *      platformProperties [2] IMPLICIT SEQUENCE(SIZE(1..CONFIGMAX)) OF Properties OPTIONAL,
+ *      platformPropertiesUri [3] IMPLICIT URIReference OPTIONAL }
+ * 
+ */ +public class PlatformConfigurationV2 extends PlatformConfiguration { + + private static final int COMPONENT_IDENTIFIER = 0; + private static final int COMPONENT_IDENTIFIER_URI = 1; + private static final int PLATFORM_PROPERTIES = 2; + private static final int PLATFORM_PROPERTIES_URI = 3; + + /** + * Constructor given the SEQUENCE that contains Platform Configuration. + * @param sequence containing the the Platform Configuration. + * @throws IllegalArgumentException if there was an error on the parsing + */ + public PlatformConfigurationV2(final ASN1Sequence sequence) throws IllegalArgumentException { + //Default values + setComponentIdentifier(new ArrayList<>()); + setComponentIdentifierUri(null); + setPlatformProperties(new ArrayList<>()); + setPlatformPropertiesUri(null); + + for (int i = 0; i < sequence.size(); i++) { + ASN1TaggedObject taggedSequence + = ASN1TaggedObject.getInstance(sequence.getObjectAt(i)); + //Set information based on the set tagged + switch (taggedSequence.getTagNo()) { + case COMPONENT_IDENTIFIER: + //Get componentIdentifier + ASN1Sequence componentConfiguration + = ASN1Sequence.getInstance(taggedSequence, false); + + //Get and set all the component values + for (int j = 0; j < componentConfiguration.size(); j++) { + //DERSequence with the components + ASN1Sequence component + = ASN1Sequence.getInstance(componentConfiguration.getObjectAt(j)); + add(new ComponentIdentifierV2(component)); + } + break; + case COMPONENT_IDENTIFIER_URI: + //Get componentIdentifierURI + ASN1Sequence componentUri = ASN1Sequence.getInstance(taggedSequence, false); + //Save Component Identifier URI + setComponentIdentifierUri(new URIReference(componentUri)); + break; + case PLATFORM_PROPERTIES: + //Get platformProperties + ASN1Sequence properties = ASN1Sequence.getInstance(taggedSequence, false); + + //Get and set all the properties values + for (int j = 0; j < properties.size(); j++) { + //DERSequence with the components + ASN1Sequence property = ASN1Sequence.getInstance(properties.getObjectAt(j)); + add(new PlatformPropertyV2(property)); + } + break; + case PLATFORM_PROPERTIES_URI: + //Get platformPropertiesURI + ASN1Sequence propertiesUri = ASN1Sequence.getInstance(taggedSequence, false); + //Save properties URI + setPlatformPropertiesUri(new URIReference(propertiesUri)); + break; + default: + break; + } + } + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("PlatformConfiguration{"); + sb.append("componentIdentifier="); + if (getComponentIdentifier().size() > 0) { + sb.append(getComponentIdentifier() + .stream() + .map(Object::toString) + .collect(Collectors.joining(","))); + } + sb.append(", componentIdentifierUri="); + if (getComponentIdentifierUri() != null) { + sb.append(getComponentIdentifierUri().toString()); + } + sb.append(", platformProperties="); + if (getPlatformProperties().size() > 0) { + sb.append(getPlatformProperties() + .stream() + .map(Object::toString) + .collect(Collectors.joining(","))); + } + sb.append(", platformPropertiesUri="); + if (getPlatformPropertiesUri() != null) { + sb.append(getPlatformPropertiesUri().toString()); + } + sb.append("}"); + + return sb.toString(); + } +} diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/attributes/V2/PlatformPropertyV2.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/attributes/V2/PlatformPropertyV2.java new file mode 100644 index 00000000..bdc23804 --- /dev/null +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/attributes/V2/PlatformPropertyV2.java @@ -0,0 +1,100 @@ +package hirs.attestationca.persist.entity.userdefined.certificate.attributes.V2; + +import hirs.attestationca.persist.entity.userdefined.certificate.attributes.PlatformProperty; +import lombok.Getter; +import lombok.Setter; +import org.bouncycastle.asn1.ASN1Enumerated; +import org.bouncycastle.asn1.ASN1Sequence; +import org.bouncycastle.asn1.DERUTF8String; + +/** + * + * Basic class that handles a single property for the platform configuration. + *
+ * Properties ::= SEQUENCE {
+ *      propertyName UTF8String (SIZE (1..STRMAX)),
+ *      propertyValue UTF8String (SIZE (1..STRMAX),
+ *      status [0] IMPLICIT AttributeStatus OPTIONAL }
+ *
+ * 
+ */ +public class PlatformPropertyV2 extends PlatformProperty { + + @Getter + @Setter + private AttributeStatus attributeStatus; + + /** + * Default constructor. + */ + public PlatformPropertyV2() { + super(); + this.attributeStatus = AttributeStatus.EMPTY_STATUS; + } + + /** + * Constructor given the name and value for the platform property. + * + * @param propertyName string containing the property name + * @param propertyValue string containing the property value + * @param attributeStatus enumerated object with the status of the property + */ + public PlatformPropertyV2(final DERUTF8String propertyName, final DERUTF8String propertyValue, + final AttributeStatus attributeStatus) { + super(propertyName, propertyValue); + this.attributeStatus = attributeStatus; + } + + /** + * Constructor given the SEQUENCE that contains the name and value for the + * platform property. + * + * @param sequence containing the name and value of the platform property + * @throws IllegalArgumentException if there was an error on the parsing + */ + public PlatformPropertyV2(final ASN1Sequence sequence) throws IllegalArgumentException { + // Check if the sequence contains the two values required + if (sequence.size() < IDENTIFIER_NUMBER) { + throw new IllegalArgumentException("Platform properties does not contain all " + + "the required fields."); + } + + setPropertyName(DERUTF8String.getInstance(sequence.getObjectAt(0))); + setPropertyValue(DERUTF8String.getInstance(sequence.getObjectAt(1))); + + // optional value which is a placeholder for now + if (sequence.size() > IDENTIFIER_NUMBER + && sequence.getObjectAt(2) instanceof ASN1Enumerated) { + ASN1Enumerated enumerated = ASN1Enumerated.getInstance(sequence.getObjectAt(2)); + this.attributeStatus = AttributeStatus.values()[enumerated.getValue().intValue()]; + } + } + + /** + * @return true if the property has been modified. + */ + public final boolean isModified() { + return getAttributeStatus() == AttributeStatus.MODIFIED; + } + + /** + * @return true if the property has been removed. + */ + public final boolean isRemoved() { + return getAttributeStatus() != AttributeStatus.REMOVED; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("PlatformPropertyV2{"); + sb.append("PropertyName=").append(getPropertyName().getString()); + sb.append(", propertyValue=").append(getPropertyValue().getString()); + if (attributeStatus != null) { + sb.append(", attributeStatus=").append(attributeStatus.toString()); + } + sb.append("}"); + + return sb.toString(); + } +} diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/attributes/V2/package-info.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/attributes/V2/package-info.java new file mode 100644 index 00000000..59110856 --- /dev/null +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/attributes/V2/package-info.java @@ -0,0 +1 @@ +package hirs.attestationca.persist.entity.userdefined.certificate.attributes.V2; \ No newline at end of file diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/attributes/package-info.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/attributes/package-info.java new file mode 100644 index 00000000..8a827d6d --- /dev/null +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/attributes/package-info.java @@ -0,0 +1 @@ +package hirs.attestationca.persist.entity.userdefined.certificate.attributes; \ No newline at end of file diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/package-info.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/package-info.java new file mode 100644 index 00000000..fc0e99e0 --- /dev/null +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/package-info.java @@ -0,0 +1 @@ +package hirs.attestationca.persist.entity.userdefined.certificate; \ No newline at end of file diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/info/FirmwareInfo.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/info/FirmwareInfo.java new file mode 100644 index 00000000..40c5357d --- /dev/null +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/info/FirmwareInfo.java @@ -0,0 +1,60 @@ +package hirs.attestationca.persist.entity.userdefined.info; + +import hirs.attestationca.persist.entity.userdefined.report.DeviceInfoReport; +import hirs.utils.StringValidator; +import jakarta.persistence.Column; +import jakarta.xml.bind.annotation.XmlElement; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.ToString; + +import java.io.Serializable; + +/** + * Used for representing the firmware info of a device, such as the BIOS information. + */ +@ToString +@EqualsAndHashCode +@Getter +public class FirmwareInfo implements Serializable { + + @XmlElement + @Column(length = DeviceInfoReport.LONG_STRING_LENGTH, nullable = false) + private final String biosVendor; + + @XmlElement + @Column(length = DeviceInfoReport.LONG_STRING_LENGTH, nullable = false) + private final String biosVersion; + + @XmlElement + @Column(length = DeviceInfoReport.SHORT_STRING_LENGTH, nullable = false) + private final String biosReleaseDate; + + /** + * Constructor used to create a populated firmware info object. + * + * @param biosVendor String bios vendor name, i.e. Dell Inc. + * @param biosVersion String bios version info, i.e. A11 + * @param biosReleaseDate String bios release date info, i.e. 03/12/2013 + */ + public FirmwareInfo(final String biosVendor, final String biosVersion, + final String biosReleaseDate) { + this.biosVendor = StringValidator.check(biosVendor, "biosVendor") + .notBlank().maxLength(DeviceInfoReport.LONG_STRING_LENGTH).getValue(); + + this.biosVersion = StringValidator.check(biosVersion, "biosVersion") + .notBlank().maxLength(DeviceInfoReport.LONG_STRING_LENGTH).getValue(); + + this.biosReleaseDate = StringValidator.check(biosReleaseDate, "biosReleaseDate") + .notBlank().maxLength(DeviceInfoReport.SHORT_STRING_LENGTH).getValue(); + } + + /** + * Default constructor, useful for hibernate and marshalling and unmarshalling. + */ + public FirmwareInfo() { + this(DeviceInfoReport.NOT_SPECIFIED, + DeviceInfoReport.NOT_SPECIFIED, + DeviceInfoReport.NOT_SPECIFIED); + } +} diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/info/HardwareInfo.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/info/HardwareInfo.java new file mode 100644 index 00000000..03632284 --- /dev/null +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/info/HardwareInfo.java @@ -0,0 +1,122 @@ +package hirs.attestationca.persist.entity.userdefined.info; + +import hirs.attestationca.persist.entity.userdefined.report.DeviceInfoReport; +import hirs.utils.StringValidator; +import jakarta.persistence.Column; +import jakarta.persistence.Embeddable; +import jakarta.xml.bind.annotation.XmlElement; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import org.apache.commons.lang3.StringUtils; + +import java.io.Serializable; + +/** + * Used for representing the hardware info of a device. + */ +@EqualsAndHashCode +@Getter +@Embeddable +public class HardwareInfo implements Serializable { + + @XmlElement + @Column(length = DeviceInfoReport.LONG_STRING_LENGTH, nullable = false) + private String manufacturer = DeviceInfoReport.NOT_SPECIFIED; + + @XmlElement + @Column(length = DeviceInfoReport.LONG_STRING_LENGTH, nullable = false) + private String productName = DeviceInfoReport.NOT_SPECIFIED; + + @XmlElement + @Column(length = DeviceInfoReport.MED_STRING_LENGTH, nullable = false) + private String version = DeviceInfoReport.NOT_SPECIFIED; + + @XmlElement + @Column(length = DeviceInfoReport.LONG_STRING_LENGTH, nullable = false) + private String systemSerialNumber = DeviceInfoReport.NOT_SPECIFIED; + + @XmlElement + @Column(length = DeviceInfoReport.LONG_STRING_LENGTH, nullable = false) + private String chassisSerialNumber = DeviceInfoReport.NOT_SPECIFIED; + + @XmlElement + @Column(length = DeviceInfoReport.LONG_STRING_LENGTH, nullable = false) + private String baseboardSerialNumber = DeviceInfoReport.NOT_SPECIFIED; + + /** + * Constructor used to create a populated firmware info object. + * + * @param manufacturer String manufacturer name + * @param productName String product name info + * @param version String bios release date info + * @param systemSerialNumber String device serial number + * @param chassisSerialNumber String device chassis serial number + * @param baseboardSerialNumber String device baseboard serial number + */ + public HardwareInfo( + final String manufacturer, + final String productName, + final String version, + final String systemSerialNumber, + final String chassisSerialNumber, + final String baseboardSerialNumber) { + if (!StringUtils.isBlank(manufacturer)) { + this.manufacturer = StringValidator.check(manufacturer, "manufacturer") + .maxLength(DeviceInfoReport.LONG_STRING_LENGTH).getValue(); + } + + if (!StringUtils.isBlank(productName)) { + this.productName = StringValidator.check(productName, "productName") + .maxLength(DeviceInfoReport.LONG_STRING_LENGTH).getValue(); + } + + if (!StringUtils.isBlank(version)) { + this.version = StringValidator.check(version, "version") + .maxLength(DeviceInfoReport.MED_STRING_LENGTH).getValue(); + } + + if (!StringUtils.isBlank(systemSerialNumber)) { + this.systemSerialNumber = StringValidator.check(systemSerialNumber, + "systemSerialNumber") + .maxLength(DeviceInfoReport.LONG_STRING_LENGTH).getValue(); + } + + if (!StringUtils.isBlank(chassisSerialNumber)) { + this.chassisSerialNumber = StringValidator.check(chassisSerialNumber, + "chassisSerialNumber") + .maxLength(DeviceInfoReport.LONG_STRING_LENGTH).getValue(); + } + + if (!StringUtils.isBlank(baseboardSerialNumber)) { + this.baseboardSerialNumber = StringValidator.check( + baseboardSerialNumber, "baseboardSerialNumber") + .maxLength(DeviceInfoReport.LONG_STRING_LENGTH).getValue(); + } + } + + /** + * Default constructor, useful for hibernate and marshalling and unmarshalling. + */ + public HardwareInfo() { + this( + DeviceInfoReport.NOT_SPECIFIED, + DeviceInfoReport.NOT_SPECIFIED, + DeviceInfoReport.NOT_SPECIFIED, + DeviceInfoReport.NOT_SPECIFIED, + DeviceInfoReport.NOT_SPECIFIED, + DeviceInfoReport.NOT_SPECIFIED + ); + } + + @Override + public String toString() { + return "HardwareInfo{" + + "manufacturer='" + manufacturer + '\'' + + ", productName='" + productName + '\'' + + ", version='" + version + '\'' + + ", systemSerialNumber='" + systemSerialNumber + '\'' + + ", chassisSerialNumber='" + chassisSerialNumber + '\'' + + ", baseboardSerialNumber='" + baseboardSerialNumber + '\'' + + '}'; + } +} diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/info/NetworkInfo.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/info/NetworkInfo.java new file mode 100644 index 00000000..73a80be0 --- /dev/null +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/info/NetworkInfo.java @@ -0,0 +1,111 @@ +package hirs.attestationca.persist.entity.userdefined.info; + +import hirs.attestationca.persist.entity.userdefined.report.DeviceInfoReport; +import jakarta.persistence.Column; +import jakarta.persistence.Embeddable; +import jakarta.xml.bind.annotation.XmlElement; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.io.Serializable; +import java.net.InetAddress; + +/** + * This class is used to represent the network info of a device. + */ +@EqualsAndHashCode +@Embeddable +public class NetworkInfo implements Serializable { + + private static final Logger LOGGER = LogManager + .getLogger(NetworkInfo.class); + + private static final int NUM_MAC_ADDRESS_BYTES = 6; + + @XmlElement + @Setter + @Getter + @Column(length = DeviceInfoReport.LONG_STRING_LENGTH, nullable = true) + private String hostname; + + @XmlElement +// @XmlJavaTypeAdapter(value = InetAddressXmlAdapter.class) + @Setter + @Getter + @Column(length = DeviceInfoReport.SHORT_STRING_LENGTH, nullable = true) +// @Convert(converter = hirs.attestationca.persist.type.InetAddressType.class) + private InetAddress ipAddress; + + @XmlElement + @Column(length = NUM_MAC_ADDRESS_BYTES, nullable = true) + @SuppressWarnings("checkstyle:magicnumber") + private byte[] macAddress; + + /** + * Constructor used to create a NetworkInfo object. + * + * @param hostname + * String representing the hostname information for the device, + * can be null if hostname unknown + * @param ipAddress + * InetAddress object representing the IP address for the device, + * can be null if IP address unknown + * @param macAddress + * byte array representing the MAC address for the device, can be + * null if MAC address is unknown + */ + public NetworkInfo(final String hostname, final InetAddress ipAddress, + final byte[] macAddress) { + setHostname(hostname); + setIpAddress(ipAddress); + setMacAddress(macAddress); + } + + /** + * Default constructor necessary for marshalling/unmarshalling XML objects. + */ + protected NetworkInfo() { + this.hostname = null; + this.ipAddress = null; + this.macAddress = null; + } + + /** + * Used to retrieve the MAC address of the device. + * + * @return a String representing the MAC address, may return null if no + * value is set + */ + public final byte[] getMacAddress() { + if (macAddress == null) { + return null; + } else { + return macAddress.clone(); + } + } + + private void setMacAddress(final byte[] macAddress) { + StringBuilder sb; + if (macAddress == null) { + sb = null; + } else { + if (macAddress.length != NUM_MAC_ADDRESS_BYTES) { + LOGGER.error( + "MAC address is only {} bytes, must be {} bytes or " + + "null", macAddress.length, + NUM_MAC_ADDRESS_BYTES); + throw new IllegalArgumentException( + "MAC address is invalid size"); + } + sb = new StringBuilder(); + for (byte b : macAddress) { + sb.append(String.format("%02X ", b)); + } + } + LOGGER.debug("setting MAC address to: {}", sb); + this.macAddress = macAddress; + } +} diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/info/OSInfo.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/info/OSInfo.java new file mode 100644 index 00000000..7350998f --- /dev/null +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/info/OSInfo.java @@ -0,0 +1,99 @@ +package hirs.attestationca.persist.entity.userdefined.info; + +import hirs.attestationca.persist.entity.userdefined.report.DeviceInfoReport; +import hirs.utils.StringValidator; +import jakarta.persistence.Column; +import jakarta.persistence.Embeddable; +import jakarta.xml.bind.annotation.XmlElement; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.ToString; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.io.Serializable; + +/** + * This class is used to represent the OS info of a device. + */ +@EqualsAndHashCode +@ToString +@Getter +@Embeddable +public class OSInfo implements Serializable { + private static final Logger LOGGER = LogManager.getLogger(OSInfo.class); + + @XmlElement + @Column(length = DeviceInfoReport.LONG_STRING_LENGTH, nullable = false) + private final String osName; + + @XmlElement + @Column(length = DeviceInfoReport.LONG_STRING_LENGTH, nullable = false) + private final String osVersion; + + @XmlElement + @Column(length = DeviceInfoReport.SHORT_STRING_LENGTH, nullable = false) + private final String osArch; + + @XmlElement + @Column(length = DeviceInfoReport.SHORT_STRING_LENGTH, nullable = true) + private final String distribution; + + @XmlElement + @Column(length = DeviceInfoReport.SHORT_STRING_LENGTH, nullable = true) + private final String distributionRelease; + + /** + * Constructor used to create an OSInfo object. This constructor takes an OS + * name (Linux | Mac OS X | Windows 7), an OS version (i.e. + * 3.10.0-123.el7.x86_64), OS architecture (x86_64), distribution (CentOS | + * Fedora), and distribution release (7.0.1406). Distribution only makes + * sense for Linux, so distribution and distributionRelease may be null. + * + * @param osName + * String OS name (Linux | Mac OS X | Windows 7) + * @param osVersion + * String OS version (i.e. 3.10.0-123.el7.x86_64) + * @param osArch + * String OS architecture (x86_64) + * @param distribution + * String distribution (CentOS | Fedora) + * @param distributionRelease + * String distribution release (7.0.1406) + */ + public OSInfo(final String osName, final String osVersion, + final String osArch, final String distribution, + final String distributionRelease) { + LOGGER.debug("setting OS name information to: {}", osName); + this.osName = StringValidator.check(osName, "osName") + .notNull().maxLength(DeviceInfoReport.LONG_STRING_LENGTH).getValue(); + + LOGGER.debug("setting OS version information to: {}", osVersion); + this.osVersion = StringValidator.check(osVersion, "osVersion") + .notNull().maxLength(DeviceInfoReport.LONG_STRING_LENGTH).getValue(); + + LOGGER.debug("setting OS arch information to: {}", osArch); + this.osArch = StringValidator.check(osArch, "osArch") + .notNull().maxLength(DeviceInfoReport.SHORT_STRING_LENGTH).getValue(); + + LOGGER.debug("setting OS distribution information to: {}", distribution); + this.distribution = StringValidator.check(distribution, "distribution") + .maxLength(DeviceInfoReport.SHORT_STRING_LENGTH).getValue(); + + LOGGER.debug("setting OS distribution release information to: {}", + distributionRelease); + this.distributionRelease = StringValidator.check(distributionRelease, "distributionRelease") + .maxLength(DeviceInfoReport.SHORT_STRING_LENGTH).getValue(); + } + + /** + * Default constructor necessary for marshalling/unmarshalling XML objects. + */ + public OSInfo() { + this(DeviceInfoReport.NOT_SPECIFIED, + DeviceInfoReport.NOT_SPECIFIED, + DeviceInfoReport.NOT_SPECIFIED, + DeviceInfoReport.NOT_SPECIFIED, + DeviceInfoReport.NOT_SPECIFIED); + } +} diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/info/RIMInfo.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/info/RIMInfo.java new file mode 100644 index 00000000..a52fb27b --- /dev/null +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/info/RIMInfo.java @@ -0,0 +1,66 @@ +package hirs.attestationca.persist.entity.userdefined.info; + +import hirs.attestationca.persist.entity.userdefined.report.DeviceInfoReport; +import hirs.utils.StringValidator; +import jakarta.persistence.Column; +import jakarta.persistence.Embeddable; +import jakarta.xml.bind.annotation.XmlElement; +import lombok.EqualsAndHashCode; +import lombok.Getter; + +import java.io.Serializable; + +@Getter +@EqualsAndHashCode +@Embeddable +public class RIMInfo implements Serializable { + + @XmlElement + @Column(length = DeviceInfoReport.MED_STRING_LENGTH, nullable = false) + private final String rimManufacturer; + + @XmlElement + @Column(length = DeviceInfoReport.MED_STRING_LENGTH, nullable = false) + private final String model; + + @XmlElement + @Column(length = DeviceInfoReport.MED_STRING_LENGTH, nullable = false) + private final String fileHash; + + @XmlElement + @Column(length = DeviceInfoReport.MED_STRING_LENGTH, nullable = false) + private final String pcrHash; + + /** + * Constructor for the initial values of the class. + * @param rimManufacturer string of the rimManufacturer + * @param model string of the model + * @param fileHash string of the file hash + * @param pcrHash string of the pcr hash + */ + public RIMInfo(final String rimManufacturer, final String model, + final String fileHash, final String pcrHash) { + this.rimManufacturer = StringValidator.check(rimManufacturer, "rimManufacturer") + .notBlank().maxLength(DeviceInfoReport.MED_STRING_LENGTH).getValue(); + this.model = StringValidator.check(model, "model") + .notBlank().maxLength(DeviceInfoReport.MED_STRING_LENGTH).getValue(); + this.fileHash = StringValidator.check(fileHash, "fileHash") + .notBlank().maxLength(DeviceInfoReport.MED_STRING_LENGTH).getValue(); + this.pcrHash = StringValidator.check(pcrHash, "pcrHash") + .notBlank().maxLength(DeviceInfoReport.MED_STRING_LENGTH).getValue(); + } + + /** + * Default no parameter constructor. + */ + public RIMInfo() { + this(DeviceInfoReport.NOT_SPECIFIED, DeviceInfoReport.NOT_SPECIFIED, + DeviceInfoReport.NOT_SPECIFIED, DeviceInfoReport.NOT_SPECIFIED); + } + + @Override + public String toString() { + return String.format("%s, %s, %s, %s", rimManufacturer, model, + fileHash, pcrHash); + } +} diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/info/TPMInfo.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/info/TPMInfo.java new file mode 100644 index 00000000..ed2d738e --- /dev/null +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/info/TPMInfo.java @@ -0,0 +1,319 @@ +package hirs.attestationca.persist.entity.userdefined.info; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import hirs.attestationca.persist.entity.userdefined.report.DeviceInfoReport; +import hirs.utils.StringValidator; +import jakarta.persistence.Column; +import jakarta.persistence.Embeddable; +import jakarta.persistence.Lob; +import jakarta.xml.bind.annotation.XmlElement; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.io.Serializable; +import java.security.cert.X509Certificate; + +/** + * This class is used to represent the TPM information for a device. + */ +@Getter +@EqualsAndHashCode +@Embeddable +public class TPMInfo implements Serializable { + private static final Logger LOGGER = LogManager.getLogger(TPMInfo.class); + private static final int MAX_BLOB_SIZE = 55535; + + @XmlElement + @Column(length = DeviceInfoReport.MED_STRING_LENGTH, nullable = true) + private String tpmMake; + + @XmlElement + @Column(nullable = true) + private short tpmVersionMajor; + + @XmlElement + @Column(nullable = true) + private short tpmVersionMinor; + + @XmlElement + @Column(nullable = true) + private short tpmVersionRevMajor; + + @XmlElement + @Column(nullable = true) + private short tpmVersionRevMinor; + + @XmlElement +// @XmlJavaTypeAdapter(X509CertificateAdapter.class) + @Lob +// @Type(type = "hirs.attestationca.persist.type.X509CertificateType") + @JsonIgnore + private X509Certificate identityCertificate; + + @Column(nullable = true, length = MAX_BLOB_SIZE) + @Lob + private byte[] pcrValues; + + @Column(nullable = true, length = MAX_BLOB_SIZE) + @Lob + private byte[] tpmQuoteHash; + + @Column(nullable = true, length = MAX_BLOB_SIZE) + @Lob + private byte[] tpmQuoteSignature; + + /** + * Constructor used to create a TPMInfo object. + * + * @param tpmMake + * String representing the make information for the TPM, + * NullPointerException thrown if null + * @param tpmVersionMajor + * short representing the major version number for the TPM + * @param tpmVersionMinor + * short representing the minor version number for the TPM + * @param tpmVersionRevMajor + * short representing the major revision number for the TPM + * @param tpmVersionRevMinor + * short representing the minor revision number for the TPM + * @param identityCertificate + * byte array with the value of the identity certificate + * @param pcrValues + * short representing the major revision number for the TPM + * @param tpmQuoteHash + * short representing the minor revision number for the TPM + * @param tpmQuoteSignature + * byte array with the value of the identity certificate + */ + @SuppressWarnings("parameternumber") + public TPMInfo(final String tpmMake, final short tpmVersionMajor, + final short tpmVersionMinor, final short tpmVersionRevMajor, + final short tpmVersionRevMinor, + final X509Certificate identityCertificate, final byte[] pcrValues, + final byte[] tpmQuoteHash, final byte[] tpmQuoteSignature) { + setTPMMake(tpmMake); + setTPMVersionMajor(tpmVersionMajor); + setTPMVersionMinor(tpmVersionMinor); + setTPMVersionRevMajor(tpmVersionRevMajor); + setTPMVersionRevMinor(tpmVersionRevMinor); + setIdentityCertificate(identityCertificate); + setPcrValues(pcrValues); + setTpmQuoteHash(tpmQuoteHash); + setTpmQuoteSignature(tpmQuoteSignature); + } + + /** + * Constructor used to create a TPMInfo object without an identity + * certificate. + * + * @param tpmMake + * String representing the make information for the TPM, + * NullPointerException thrown if null + * @param tpmVersionMajor + * short representing the major version number for the TPM + * @param tpmVersionMinor + * short representing the minor version number for the TPM + * @param tpmVersionRevMajor + * short representing the major revision number for the TPM + * @param tpmVersionRevMinor + * short representing the minor revision number for the TPM + * @param pcrValues + * short representing the major revision number for the TPM + * @param tpmQuoteHash + * short representing the minor revision number for the TPM + * @param tpmQuoteSignature + * byte array with the value of the identity certificate + */ + @SuppressWarnings("parameternumber") + public TPMInfo(final String tpmMake, final short tpmVersionMajor, + final short tpmVersionMinor, final short tpmVersionRevMajor, + final short tpmVersionRevMinor, final byte[] pcrValues, + final byte[] tpmQuoteHash, final byte[] tpmQuoteSignature) { + setTPMMake(tpmMake); + setTPMVersionMajor(tpmVersionMajor); + setTPMVersionMinor(tpmVersionMinor); + setTPMVersionRevMajor(tpmVersionRevMajor); + setTPMVersionRevMinor(tpmVersionRevMinor); + setPcrValues(pcrValues); + setTpmQuoteHash(tpmQuoteHash); + setTpmQuoteSignature(tpmQuoteSignature); + } + + /** + * Constructor used to create a TPMInfo object without an identity + * certificate. + * + * @param tpmMake + * String representing the make information for the TPM, + * NullPointerException thrown if null + * @param tpmVersionMajor + * short representing the major version number for the TPM + * @param tpmVersionMinor + * short representing the minor version number for the TPM + * @param tpmVersionRevMajor + * short representing the major revision number for the TPM + * @param tpmVersionRevMinor + * short representing the minor revision number for the TPM + */ + public TPMInfo(final String tpmMake, final short tpmVersionMajor, + final short tpmVersionMinor, final short tpmVersionRevMajor, + final short tpmVersionRevMinor) { + this(tpmMake, tpmVersionMajor, tpmVersionMinor, tpmVersionRevMajor, + tpmVersionRevMinor, null, + new byte[0], new byte[0], new byte[0]); + } + + /** + * Constructor used to create a TPMInfo object without an identity + * certificate. + * + * @param tpmMake + * String representing the make information for the TPM, + * NullPointerException thrown if null + * @param tpmVersionMajor + * short representing the major version number for the TPM + * @param tpmVersionMinor + * short representing the minor version number for the TPM + * @param tpmVersionRevMajor + * short representing the major revision number for the TPM + * @param tpmVersionRevMinor + * short representing the minor revision number for the TPM + * @param identityCertificate + * byte array with the value of the identity certificate + */ + public TPMInfo(final String tpmMake, final short tpmVersionMajor, + final short tpmVersionMinor, final short tpmVersionRevMajor, + final short tpmVersionRevMinor, + final X509Certificate identityCertificate) { + this(tpmMake, tpmVersionMajor, tpmVersionMinor, tpmVersionRevMajor, + tpmVersionRevMinor, identityCertificate, + new byte[0], new byte[0], new byte[0]); + } + + /** + * Default constructor used for marshalling/unmarshalling XML objects. + */ + public TPMInfo() { + this(DeviceInfoReport.NOT_SPECIFIED, + (short) 0, + (short) 0, + (short) 0, + (short) 0, + new byte[0], + new byte[0], + new byte[0]); + identityCertificate = null; + } + + /** + * Getter for the tpmQuote passed up by the client. + * @return a byte blob of quote + */ + public final byte[] getTpmQuoteHash() { + return tpmQuoteHash.clone(); + } + + /** + * Getter for the quote signature. + * @return a byte blob. + */ + public final byte[] getTpmQuoteSignature() { + return tpmQuoteSignature.clone(); + } + + /** + * Getter for the pcr values. + * @return a byte blob for the pcrValues. + */ + public final byte[] getPcrValues() { + return pcrValues.clone(); + } + + private void setTPMMake(final String tpmMake) { + LOGGER.debug("setting TPM make info: {}", tpmMake); + this.tpmMake = StringValidator.check(tpmMake, "tpmMake") + .notNull().maxLength(DeviceInfoReport.MED_STRING_LENGTH).getValue(); + } + + private void setTPMVersionMajor(final short tpmVersionMajor) { + if (tpmVersionMajor < 0) { + LOGGER.error("TPM major version number cannot be negative: {}", + tpmVersionMajor); + throw new IllegalArgumentException( + "negative TPM major version number"); + } + LOGGER.debug("setting TPM major version number: {}", tpmVersionMajor); + this.tpmVersionMajor = tpmVersionMajor; + } + + private void setTPMVersionMinor(final short tpmVersionMinor) { + if (tpmVersionMinor < 0) { + LOGGER.error("TPM minor version number cannot be negative: {}", + tpmVersionMinor); + throw new IllegalArgumentException( + "negative TPM minor version number"); + } + LOGGER.debug("setting TPM minor version number: {}", tpmVersionMinor); + this.tpmVersionMinor = tpmVersionMinor; + } + + private void setTPMVersionRevMajor(final short tpmVersionRevMajor) { + if (tpmVersionRevMajor < 0) { + LOGGER.error("TPM major revision number cannot be negative: {}", + tpmVersionRevMajor); + throw new IllegalArgumentException( + "negative TPM major revision number"); + } + LOGGER.debug("setting TPM major revision version number: {}", + tpmVersionRevMajor); + this.tpmVersionRevMajor = tpmVersionRevMajor; + } + + private void setTPMVersionRevMinor(final short tpmVersionRevMinor) { + if (tpmVersionRevMinor < 0) { + LOGGER.error("TPM minor revision number cannot be negative: {}", + tpmVersionRevMinor); + throw new IllegalArgumentException( + "negative TPM minor revision number"); + } + LOGGER.debug("setting TPM minor revision version number: {}", + tpmVersionRevMinor); + this.tpmVersionRevMinor = tpmVersionRevMinor; + } + + private void setIdentityCertificate( + final X509Certificate identityCertificate) { + if (identityCertificate == null) { + LOGGER.error("identity certificate cannot be null"); + throw new NullPointerException("identityCertificate"); + } + LOGGER.debug("setting identity certificate"); + this.identityCertificate = identityCertificate; + } + + private void setPcrValues(final byte[] pcrValues) { + if (pcrValues == null) { + this.pcrValues = new byte[0]; + } else { + this.pcrValues = pcrValues.clone(); + } + } + + private void setTpmQuoteHash(final byte[] tpmQuoteHash) { + if (tpmQuoteHash == null) { + this.tpmQuoteHash = new byte[0]; + } else { + this.tpmQuoteHash = tpmQuoteHash.clone(); + } + } + + private void setTpmQuoteSignature(final byte[] tpmQuoteSignature) { + if (tpmQuoteSignature == null) { + this.tpmQuoteSignature = new byte[0]; + } else { + this.tpmQuoteSignature = tpmQuoteSignature.clone(); + } + } +} diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/package-info.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/package-info.java new file mode 100644 index 00000000..fd20b8af --- /dev/null +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/package-info.java @@ -0,0 +1 @@ +package hirs.attestationca.persist.entity.userdefined; \ No newline at end of file diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/report/DeviceInfoReport.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/report/DeviceInfoReport.java new file mode 100644 index 00000000..e47fedb1 --- /dev/null +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/report/DeviceInfoReport.java @@ -0,0 +1,289 @@ +package hirs.attestationca.persist.entity.userdefined.report; + +import hirs.attestationca.persist.entity.userdefined.Report; +import hirs.attestationca.persist.entity.userdefined.info.FirmwareInfo; +import hirs.attestationca.persist.entity.userdefined.info.HardwareInfo; +import hirs.attestationca.persist.entity.userdefined.info.NetworkInfo; +import hirs.attestationca.persist.entity.userdefined.info.OSInfo; +import hirs.attestationca.persist.entity.userdefined.info.TPMInfo; +import hirs.utils.VersionHelper; +import jakarta.persistence.Column; +import jakarta.persistence.Embedded; +import jakarta.persistence.Entity; +import jakarta.persistence.Transient; +import lombok.Getter; +import lombok.Setter; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.io.Serializable; + + +/** + * A DeviceInfoReport is a Report used to transfer the + * information about the device. This Report includes the network, + * OS, and TPM information. + */ +@Entity +public class DeviceInfoReport extends Report implements Serializable { + + private static final Logger LOGGER = LogManager.getLogger(DeviceInfoReport.class); + + /** + * A variable used to describe unavailable hardware, firmware, or OS info. + */ + public static final String NOT_SPECIFIED = "Not Specified"; + /** + * Constant variable representing the various Short sized strings. + */ + public static final int SHORT_STRING_LENGTH = 32; + /** + * Constant variable representing the various Medium sized strings. + */ + public static final int MED_STRING_LENGTH = 64; + /** + * Constant variable representing the various Long sized strings. + */ + public static final int LONG_STRING_LENGTH = 255; + + @Embedded + private NetworkInfo networkInfo; + + @Embedded + private OSInfo osInfo; + + @Embedded + private FirmwareInfo firmwareInfo; + + @Embedded + private HardwareInfo hardwareInfo; + + @Embedded + private TPMInfo tpmInfo; + + @Getter + @Column(nullable = false) + private String clientApplicationVersion; + + @Getter + @Setter + @Transient + private String paccorOutputString; + + /** + * Default constructor necessary for marshalling/unmarshalling. + */ + public DeviceInfoReport() { + /* do nothing */ + } + + /** + * Constructor used to create a DeviceInfoReport. The + * information cannot be changed after the DeviceInfoReport is + * created. + * + * @param networkInfo + * NetworkInfo object, cannot be null + * @param osInfo + * OSInfo object, cannot be null + * @param firmwareInfo + * FirmwareInfo object, cannot be null + * @param hardwareInfo + * HardwareInfo object, cannot be null + * @param tpmInfo + * TPMInfo object, may be null if a TPM is not available on the + * device + */ + public DeviceInfoReport(final NetworkInfo networkInfo, final OSInfo osInfo, + final FirmwareInfo firmwareInfo, final HardwareInfo hardwareInfo, + final TPMInfo tpmInfo) { + this(networkInfo, osInfo, firmwareInfo, hardwareInfo, tpmInfo, VersionHelper.getVersion()); + } + + /** + * Constructor used to create a DeviceInfoReport. The + * information cannot be changed after the DeviceInfoReport is + * created. + * + * @param networkInfo + * NetworkInfo object, cannot be null + * @param osInfo + * OSInfo object, cannot be null + * @param firmwareInfo + * FirmwareInfo object, cannot be null + * @param hardwareInfo + * HardwareInfo object, cannot be null + * @param tpmInfo + * TPMInfo object, may be null if a TPM is not available on the + * device + * @param clientApplicationVersion + * string representing the version of the client that submitted this report, + * cannot be null + */ + public DeviceInfoReport(final NetworkInfo networkInfo, final OSInfo osInfo, + final FirmwareInfo firmwareInfo, final HardwareInfo hardwareInfo, + final TPMInfo tpmInfo, final String clientApplicationVersion) { + setNetworkInfo(networkInfo); + setOSInfo(osInfo); + setFirmwareInfo(firmwareInfo); + setHardwareInfo(hardwareInfo); + setTPMInfo(tpmInfo); + this.clientApplicationVersion = clientApplicationVersion; + } + + /** + * Retrieves the NetworkInfo for this DeviceInfoReport. + * + * @return networkInfo + */ + public NetworkInfo getNetworkInfo() { + /* + * Hibernate bug requires this + * https://hibernate.atlassian.net/browse/HHH-7610 + * without null may be returned, which this interface does not support + */ + if (networkInfo == null) { + networkInfo = new NetworkInfo(null, null, null); + } + return networkInfo; + } + + /** + * Retrieves the OSInfo for this DeviceInfoReport. + * + * @return osInfo + */ + public OSInfo getOSInfo() { + /* + * Hibernate bug requires this + * https://hibernate.atlassian.net/browse/HHH-7610 + * without null may be returned, which this interface does not support + */ + if (osInfo == null) { + osInfo = new OSInfo(NOT_SPECIFIED, NOT_SPECIFIED, + NOT_SPECIFIED, NOT_SPECIFIED, NOT_SPECIFIED); + } + return osInfo; + } + + /** + * Retrieves the FirmwareInfo for this DeviceInfoReport. + * + * @return osInfo + */ + public FirmwareInfo getFirmwareInfo() { + /* + * Hibernate bug requires this + * https://hibernate.atlassian.net/browse/HHH-7610 + * without null may be returned, which this interface does not support + */ + if (firmwareInfo == null) { + firmwareInfo = new FirmwareInfo(NOT_SPECIFIED, + NOT_SPECIFIED, NOT_SPECIFIED); + } + return firmwareInfo; + } + + /** + * Retrieves the OSInfo for this DeviceInfoReport. + * + * @return osInfo + */ + public HardwareInfo getHardwareInfo() { + /* + * Hibernate bug requires this + * https://hibernate.atlassian.net/browse/HHH-7610 + * without null may be returned, which this interface does not support + */ + if (hardwareInfo == null) { + hardwareInfo = new HardwareInfo( + NOT_SPECIFIED, + NOT_SPECIFIED, + NOT_SPECIFIED, + NOT_SPECIFIED, + NOT_SPECIFIED, + NOT_SPECIFIED + ); + } + return hardwareInfo; + } + + /** + * Retrieves the TPMInfo for this DeviceInfoReport. TPMInfo may + * be null if a TPM is not available on the device. + * + * @return tpmInfo, may be null if a TPM is not available on the device + */ + public TPMInfo getTPMInfo() { + return tpmInfo; + } + + @Override + public String getReportType() { + return this.getClass().getName(); + } + + /** + * Searches the given set of TPMBaselines for matching device info fields that + * are determined critical to detecting a kernel update. + * @param tpmBaselines Iterable<TPMBaseline> set of TPMBaseline objects. + * @return True, if one of the TPM baselines in the set has the same kernel-specific + * info as this DeviceInfoReport. + */ + public final boolean matchesKernelInfo() { //final Iterable tpmBaselines) { + boolean match = false; + +// if (tpmBaselines != null) { + // Retrieve the fields which indicate a kernel update +// final OSInfo kernelOSInfo = getOSInfo(); + + // perform the search +// for (final TpmWhiteListBaseline baseline : tpmBaselines) { +// final OSInfo baselineOSInfo = baseline.getOSInfo(); +// if(baselineOSInfo.getOSName().equalsIgnoreCase(kernelOSInfo.getOSName()) +// && baselineOSInfo.getOSVersion().equalsIgnoreCase(kernelOSInfo.getOSVersion())) { +// match = true; +// break; +// } +// } +// } + + return match; + } + + private void setNetworkInfo(NetworkInfo networkInfo) { + if (networkInfo == null) { + LOGGER.error("NetworkInfo cannot be null"); + throw new NullPointerException("network info"); + } + this.networkInfo = networkInfo; + } + + private void setOSInfo(OSInfo osInfo) { + if (osInfo == null) { + LOGGER.error("OSInfo cannot be null"); + throw new NullPointerException("os info"); + } + this.osInfo = osInfo; + } + + private void setFirmwareInfo(FirmwareInfo firmwareInfo) { + if (firmwareInfo == null) { + LOGGER.error("FirmwareInfo cannot be null"); + throw new NullPointerException("firmware info"); + } + this.firmwareInfo = firmwareInfo; + } + + private void setHardwareInfo(HardwareInfo hardwareInfo) { + if (hardwareInfo == null) { + LOGGER.error("HardwareInfo cannot be null"); + throw new NullPointerException("hardware info"); + } + this.hardwareInfo = hardwareInfo; + } + + private void setTPMInfo(TPMInfo tpmInfo) { + this.tpmInfo = tpmInfo; + } +} diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/result/CertificateValidationResult.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/result/CertificateValidationResult.java new file mode 100644 index 00000000..39d75739 --- /dev/null +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/result/CertificateValidationResult.java @@ -0,0 +1,51 @@ +package hirs.attestationca.persist.entity.userdefined.result; + +import lombok.Getter; +import lombok.Setter; + +/** + * An CertificateValidationResult represents the result of a certificate validation + * operation. + * + */ +@Getter +@Setter +public class CertificateValidationResult { + /** + * Enum used to represent certificate validation status. + */ + public enum CertificateValidationStatus { + + /** + * Represents a passing validation. + */ + PASS, + + /** + * Represents a failed validation. + */ + FAIL, + + /** + * Represents a validation error. + */ + ERROR + } + + private CertificateValidationStatus validationStatus; + private String validationResultMessage; + + + /** + * Sets the certificate validation status and result message. + * + * @param status enum representing the certificate validation status + * @param resultMessage String representing certificate validation message + */ + public final void setCertValidationStatusAndResultMessage( + final CertificateValidationStatus status, + final String resultMessage) { + this.validationStatus = status; + this.validationResultMessage = resultMessage; + } +} diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/rim/BaseReferenceManifest.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/rim/BaseReferenceManifest.java new file mode 100644 index 00000000..81b67edf --- /dev/null +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/rim/BaseReferenceManifest.java @@ -0,0 +1,390 @@ +package hirs.attestationca.persist.entity.userdefined.rim; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import hirs.attestationca.persist.entity.userdefined.ReferenceManifest; +import hirs.attestationca.persist.service.ReferenceManifestServiceImpl; +import hirs.utils.SwidResource; +import hirs.utils.xjc.BaseElement; +import hirs.utils.xjc.Directory; +import hirs.utils.xjc.File; +import hirs.utils.xjc.FilesystemItem; +import hirs.utils.xjc.Link; +import hirs.utils.xjc.Meta; +import hirs.utils.xjc.ResourceCollection; +import hirs.utils.xjc.SoftwareIdentity; +import hirs.utils.xjc.SoftwareMeta; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.xml.bind.JAXBContext; +import jakarta.xml.bind.JAXBElement; +import jakarta.xml.bind.JAXBException; +import jakarta.xml.bind.UnmarshalException; +import jakarta.xml.bind.Unmarshaller; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import javax.xml.namespace.QName; +import javax.xml.validation.Schema; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.Base64; +import java.util.List; +import java.util.Map; + +/** + * + */ +@Getter +@Setter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@Entity +public class BaseReferenceManifest extends ReferenceManifest { + + private static final Logger LOGGER = LogManager.getLogger(BaseReferenceManifest.class); + /** + * Holds the name of the 'base64Hash' field. + */ + public static final String BASE_64_HASH_FIELD = "base64Hash"; + + private static JAXBContext jaxbContext; + + @Column + @JsonIgnore + private String base64Hash = ""; + @Column + private String swidName = null; + @Column + private int swidCorpus = 0; + @Column + private String colloquialVersion = null; + @Column + private String product = null; + @Column + private String revision = null; + @Column + private String edition = null; + @Column + private String rimLinkHash = null; + @Column + private String bindingSpec = null; + @Column + private String bindingSpecVersion = null; + @Column + private String platformVersion = null; + @Column + private String payloadType = null; + @Column + private String pcURIGlobal = null; + @Column + private String pcURILocal = null; + + private String entityName = null; + private String entityRegId = null; + private String entityRole = null; + private String entityThumbprint = null; + private String linkHref = null; + private String linkRel = null; + + /** + * Support constructor for the RIM object. + * + * @param fileName - string representation of the uploaded file. + * @param rimBytes - the file content of the uploaded file. + * @throws IOException - thrown if the file is invalid. + */ + public BaseReferenceManifest(final String fileName, final byte[] rimBytes) throws IOException { + this(rimBytes); + this.setFileName(fileName); + } + + /** + * Main constructor for the RIM object. This takes in a byte array of a + * valid swidtag file and parses the information. + * + * @param rimBytes byte array representation of the RIM + * @throws IOException if unable to unmarshal the string + */ + @SuppressWarnings("checkstyle:AvoidInlineConditionals") + public BaseReferenceManifest(final byte[] rimBytes) throws IOException { + super(rimBytes); + this.setRimType(BASE_RIM); + this.setFileName(""); + SoftwareIdentity si = validateSwidTag(new ByteArrayInputStream(rimBytes)); + + MessageDigest digest = null; + this.base64Hash = ""; + try { + digest = MessageDigest.getInstance("SHA-256"); + this.base64Hash = Base64.getEncoder().encodeToString( + digest.digest(rimBytes)); + } catch (NoSuchAlgorithmException noSaEx) { + LOGGER.error(noSaEx); + } + + // begin parsing valid swid tag + if (si != null) { + setTagId(si.getTagId()); + this.swidName = si.getName(); + this.swidCorpus = si.isCorpus() ? 1 : 0; + this.setSwidPatch(si.isPatch()); + this.setSwidSupplemental(si.isSupplemental()); + this.setSwidVersion(si.getVersion()); + if (si.getTagVersion() != null) { + this.setSwidTagVersion(si.getTagVersion().toString()); + } + + for (Object object : si.getEntityOrEvidenceOrLink()) { + if (object instanceof JAXBElement) { + JAXBElement element = (JAXBElement) object; + String elementName = element.getName().getLocalPart(); + switch (elementName) { + case "Meta": + parseSoftwareMeta((SoftwareMeta) element.getValue()); + break; + case "Entity": + hirs.utils.xjc.Entity entity + = (hirs.utils.xjc.Entity) element.getValue(); + if (entity != null) { + this.entityName = entity.getName(); + this.entityRegId = entity.getRegid(); + StringBuilder sb = new StringBuilder(); + for (String role : entity.getRole()) { + sb.append(String.format("%s%n", role)); + } + this.entityRole = sb.toString(); + this.entityThumbprint = entity.getThumbprint(); + } + break; + case "Link": + Link link + = (Link) element.getValue(); + if (link != null) { + this.linkHref = link.getHref(); + this.linkRel = link.getRel(); + } + break; + case "Payload": + parseResource((ResourceCollection) element.getValue()); + break; + case "Signature": + // left blank for a followup issue enhancement + default: + } + } + } + } + } + + /** + * This is a helper method that parses the SoftwareMeta tag and stores the + * information in the class fields. + * + * @param softwareMeta The object to parse. + */ + private void parseSoftwareMeta(final SoftwareMeta softwareMeta) { + if (softwareMeta != null) { + for (Map.Entry entry + : softwareMeta.getOtherAttributes().entrySet()) { + switch (entry.getKey().getLocalPart()) { + case "colloquialVersion": + this.colloquialVersion = entry.getValue(); + break; + case "product": + this.product = entry.getValue(); + break; + case "revision": + this.revision = entry.getValue(); + break; + case "edition": + this.edition = entry.getValue(); + break; + case "rimLinkHash": + this.rimLinkHash = entry.getValue(); + break; + case "bindingSpec": + this.bindingSpec = entry.getValue(); + break; + case "bindingSpecVersion": + this.bindingSpecVersion = entry.getValue(); + break; + case "platformManufacturerId": + this.setPlatformManufacturerId(entry.getValue()); + break; + case "platformModel": + this.setPlatformModel(entry.getValue()); + break; + case "platformManufacturerStr": + this.setPlatformManufacturer(entry.getValue()); + break; + case "platformVersion": + this.platformVersion = entry.getValue(); + break; + case "payloadType": + this.payloadType = entry.getValue(); + break; + case "pcURIGlobal": + this.pcURIGlobal = entry.getValue(); + break; + case "pcURILocal": + this.pcURILocal = entry.getValue(); + break; + default: + } + } + } + } + + /** + * This method and code is pulled and adopted from the TCG Tool. Since this + * is taking in an file stored in memory through http, this was changed from + * a file to a stream as the input. + * + * @param fileStream stream of the swidtag file. + * @return a {@link SoftwareIdentity} object + * @throws IOException Thrown by the unmarhsallSwidTag method. + */ + private SoftwareIdentity validateSwidTag(final InputStream fileStream) throws IOException { + JAXBElement jaxbe = unmarshallSwidTag(fileStream); + SoftwareIdentity swidTag = (SoftwareIdentity) jaxbe.getValue(); + + LOGGER.info(String.format("SWID Tag found: %nname: %s;%ntagId: %s%n%s", + swidTag.getName(), swidTag.getTagId(), SCHEMA_STATEMENT)); + return swidTag; + } + + /** + * Helper method that is used to parse a specific element of the SwidTag + * based on an already established and stored byte array. + * + * @param elementName string of an xml tag in the file. + * @return the object value of the element, if it exists + */ + private BaseElement getBaseElementFromBytes(final String elementName) { + BaseElement baseElement = null; + + if (getRimBytes() != null && elementName != null) { + try { + SoftwareIdentity si = validateSwidTag(new ByteArrayInputStream(getRimBytes())); + JAXBElement element; + for (Object object : si.getEntityOrEvidenceOrLink()) { + if (object instanceof JAXBElement) { + element = (JAXBElement) object; + if (element.getName().getLocalPart().equals(elementName)) { + // found the element + baseElement = (BaseElement) element.getValue(); + } + } + } + + } catch (IOException ioEx) { + LOGGER.error("Failed to parse Swid Tag bytes.", ioEx); + } + } + + return baseElement; + } + + /** + * This method unmarshalls the swidtag found at [path] and validates it + * according to the schema. + * + * @param stream to the input swidtag + * @return the SoftwareIdentity element at the root of the swidtag + * @throws IOException if the swidtag cannot be unmarshalled or validated + */ + private JAXBElement unmarshallSwidTag(final InputStream stream) throws IOException { + JAXBElement jaxbe = null; + Schema schema; + + try { + schema = ReferenceManifestServiceImpl.getSchemaObject(); + if (jaxbContext == null) { + jaxbContext = JAXBContext.newInstance(SCHEMA_PACKAGE); + } + Unmarshaller unmarshaller = jaxbContext.createUnmarshaller(); + unmarshaller.setSchema(schema); + jaxbe = (JAXBElement) unmarshaller.unmarshal(stream); + } catch (UnmarshalException umEx) { + LOGGER.error(String.format("Error validating swidtag file!%n%s%n%s", + umEx.getMessage(), umEx.toString())); + for (StackTraceElement ste : umEx.getStackTrace()) { + LOGGER.error(ste.toString()); + } + } catch (IllegalArgumentException iaEx) { + LOGGER.error("Input file empty."); + } catch (JAXBException jaxEx) { + for (StackTraceElement ste : jaxEx.getStackTrace()) { + LOGGER.error(ste.toString()); + } + } + + if (jaxbe != null) { + return jaxbe; + } else { + throw new IOException("Invalid Base RIM, swidtag format expected."); + } + } + + /** + * Default method for parsing the payload element. + * + * @return a collection of payload objects. + */ + public final List parseResource() { + return parseResource((ResourceCollection) this.getBaseElementFromBytes("Payload")); + } + + /** + * This method parses the payload method of a {@link ResourceCollection}. + * + * @param rc Resource Collection object. + * @return a collection of payload objects. + */ + public final List parseResource(final ResourceCollection rc) { + List resources = new ArrayList<>(); + + try { + if (rc != null) { + for (Meta meta : rc.getDirectoryOrFileOrProcess()) { + if (meta != null) { + if (meta instanceof Directory) { + Directory directory = (Directory) meta; + for (FilesystemItem fsi : directory.getDirectoryOrFile()) { + if (fsi != null) { + resources.add(new SwidResource( + (File) fsi, null)); + } + } + } else if (meta instanceof File) { + resources.add(new SwidResource((File) meta, null)); + } + } + } + } + } catch (ClassCastException ccEx) { + LOGGER.error(ccEx); + LOGGER.error("At this time, the code does not support the " + + "particular formatting of this SwidTag's Payload."); + } + + return resources; + } + + @Override + public String toString() { + return String.format("ReferenceManifest{swidName=%s," + + "platformManufacturer=%s," + + " platformModel=%s," + + "tagId=%s, rimHash=%s}", + swidName, this.getPlatformManufacturer(), + this.getPlatformModel(), getTagId(), this.getBase64Hash()); + } +} diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/rim/EventLogMeasurements.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/rim/EventLogMeasurements.java new file mode 100644 index 00000000..f2ee5bb1 --- /dev/null +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/rim/EventLogMeasurements.java @@ -0,0 +1,64 @@ +package hirs.attestationca.persist.entity.userdefined.rim; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import hirs.attestationca.persist.entity.userdefined.ReferenceManifest; +import hirs.attestationca.persist.enums.AppraisalStatus; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import lombok.Getter; +import lombok.Setter; + +import java.io.IOException; + +/** + * Sub class that will just focus on PCR Values and Events. + * Similar to {@link SupportReferenceManifest} + * however this is the live log from the client. + */ +@Entity +public class EventLogMeasurements extends ReferenceManifest { + + @Column + @JsonIgnore + @Getter @Setter + private int pcrHash = 0; + @Enumerated(EnumType.STRING) + @Getter @Setter + private AppraisalStatus.Status overallValidationResult = AppraisalStatus.Status.FAIL; + + /** + * Support constructor for the RIM object. + * + * @param rimBytes byte array representation of the RIM + * @throws java.io.IOException if unable to unmarshal the string + */ + public EventLogMeasurements(final byte[] rimBytes) throws IOException { + this("blank.measurement", rimBytes); + } + + /** + * Support constructor for the RIM object. + * + * @param fileName - string representation of the uploaded file. + * @param rimBytes byte array representation of the RIM + * @throws java.io.IOException if unable to unmarshal the string + */ + public EventLogMeasurements(final String fileName, + final byte[] rimBytes) throws IOException { + super(rimBytes); + this.setFileName(fileName); + this.archive("Event Log Measurement"); + this.setRimType(MEASUREMENT_RIM); + this.pcrHash = 0; + } + + /** + * Default constructor necessary for Hibernate. + */ + protected EventLogMeasurements() { + super(); + this.pcrHash = 0; + } +} \ No newline at end of file diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/rim/SupportReferenceManifest.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/rim/SupportReferenceManifest.java new file mode 100644 index 00000000..eed38dd8 --- /dev/null +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/rim/SupportReferenceManifest.java @@ -0,0 +1,114 @@ +package hirs.attestationca.persist.entity.userdefined.rim; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import hirs.attestationca.persist.entity.userdefined.ReferenceManifest; +import hirs.utils.tpm.eventlog.TCGEventLog; +import hirs.utils.tpm.eventlog.TpmPcrEvent; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import lombok.Getter; +import lombok.Setter; +import lombok.extern.log4j.Log4j2; + +import java.io.IOException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; + +/** + * Sub class that will just focus on PCR Values and Events. + */ +@Log4j2 +@Getter +@Setter +@Entity +public class SupportReferenceManifest extends ReferenceManifest { + + @Column + @JsonIgnore + private int pcrHash = 0; + @Column + private boolean updated = false; + @Column + private boolean processed = false; + + /** + * Main constructor for the RIM object. This takes in a byte array of a + * valid swidtag file and parses the information. + * + * @param fileName - string representation of the uploaded file. + * @param rimBytes byte array representation of the RIM + * @throws IOException if unable to unmarshal the string + */ + public SupportReferenceManifest(final String fileName, + final byte[] rimBytes) throws IOException { + super(rimBytes); + this.setFileName(fileName); + this.setRimType(SUPPORT_RIM); + this.pcrHash = 0; + } + + /** + * Main constructor for the RIM object. This takes in a byte array of a + * valid swidtag file and parses the information. + * + * @param rimBytes byte array representation of the RIM + * @throws IOException if unable to unmarshal the string + */ + public SupportReferenceManifest(final byte[] rimBytes) throws IOException { + this("blank.rimel", rimBytes); + } + + /** + * Default constructor necessary for Hibernate. + */ + protected SupportReferenceManifest() { + super(); + this.pcrHash = 0; + } + + + /** + * Getter method for the expected PCR values contained within the support + * RIM. + * @return a string array of the pcr values. + */ + public String[] getExpectedPCRList() { + try { + TCGEventLog logProcessor = new TCGEventLog(this.getRimBytes()); + this.pcrHash = Arrays.hashCode(logProcessor.getExpectedPCRValues()); + return logProcessor.getExpectedPCRValues(); + } catch (CertificateException cEx) { + log.error(cEx); + } catch (NoSuchAlgorithmException noSaEx) { + log.error(noSaEx); + } catch (IOException ioEx) { + log.error(ioEx); + } + + return new String[0]; + } + + /** + * Getter method for the event log that should be present in the support RIM. + * + * @return list of TPM PCR Events for display + */ + public Collection getEventLog() { + TCGEventLog logProcessor = null; + try { + logProcessor = new TCGEventLog(this.getRimBytes()); + return logProcessor.getEventList(); + } catch (CertificateException cEx) { + log.error(cEx); + } catch (NoSuchAlgorithmException noSaEx) { + log.error(noSaEx); + } catch (IOException ioEx) { + log.error(ioEx); + } + + return new ArrayList<>(); + } +} diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/rim/package-info.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/rim/package-info.java new file mode 100644 index 00000000..85a64fc8 --- /dev/null +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/rim/package-info.java @@ -0,0 +1 @@ +package hirs.attestationca.persist.entity.userdefined.rim; \ No newline at end of file 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 new file mode 100644 index 00000000..0319d31a --- /dev/null +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/enums/AppraisalStatus.java @@ -0,0 +1,106 @@ +package hirs.attestationca.persist.enums; + +/** + * Class to capture appraisal results and corresponding messages. + */ +public class AppraisalStatus { + /** + * Enum used to represent appraisal status. + */ + public enum Status { + + /** + * Represents a passing appraisal. + */ + PASS, + + /** + * Represents a failed appraisal. + */ + FAIL, + + /** + * Represents an appraisal generation error. + */ + ERROR, + /** + * Represents an unknown appraisal result. + */ + UNKNOWN + } + + private Status appStatus; + private String message; + private String additionalInfo; + + /** + * Default constructor. Set appraisal status and description. + * @param appStatus status of appraisal + * @param message description of result + */ + public AppraisalStatus(final Status appStatus, final String message) { + this(appStatus, message, ""); + } + + /** + * Default constructor. Set appraisal status and description. + * @param appStatus status of appraisal + * @param message description of result + * @param additionalInfo any additional information needed to + * be passed on + */ + public AppraisalStatus(final Status appStatus, final String message, + final String additionalInfo) { + this.appStatus = appStatus; + this.message = message; + this.additionalInfo = additionalInfo; + } + + /** + * Get appraisal status. + * @return appraisal status + */ + public Status getAppStatus() { + return appStatus; + } + + /** + * Set appraisal status. + * @param appStatus new status + */ + public void setAppStatus(final Status appStatus) { + this.appStatus = appStatus; + } + + /** + * Get appraisal description message. + * @return appraisal description message + */ + public String getMessage() { + return message; + } + + /** + * Set appraisal description message. + * @param message appraisal description message + */ + public void setMessage(final String message) { + this.message = message; + } + + /** + * Getter for additional information during validation. + * @return string of additional information + */ + public String getAdditionalInfo() { + return additionalInfo; + } + + /** + * Setter for any additional information. + * @param additionalInfo the string of additional information + */ + public void setAdditionalInfo(final String additionalInfo) { + this.additionalInfo = additionalInfo; + } +} diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/enums/HealthStatus.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/enums/HealthStatus.java new file mode 100644 index 00000000..ffeffb99 --- /dev/null +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/enums/HealthStatus.java @@ -0,0 +1,57 @@ +package hirs.attestationca.persist.enums; + +import java.util.Arrays; +import java.util.stream.Collectors; + +/** + * HealthStatus is used to represent the health of a device. + */ +public enum HealthStatus { + /** + * The trusted state, no issues with the device. + */ + TRUSTED("trusted"), + + /** + * The untrusted state, there is a problem with the device. + */ + UNTRUSTED("untrusted"), + + /** + * A state for when the health has not been calculated yet. + */ + UNKNOWN("unknown"); + + private String healthStatus; + + /** + * Creates a new HealthStatus object given a String. + * + * @param healthStatus + * "trusted", "untrusted", or "unknown" + */ + HealthStatus(final String healthStatus) { + this.healthStatus = healthStatus; + } + + /** + * Returns the health status. + * + * @return the status + */ + public String getStatus() { + return this.healthStatus; + } + + @Override + public String toString() { + return getStatus(); + } + + public static boolean isValidStatus(final String healthStatus) { + return Arrays.stream(HealthStatus.values()) + .map(HealthStatus::name) + .collect(Collectors.toSet()) + .contains(healthStatus); + } +} \ No newline at end of file diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/enums/package-info.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/enums/package-info.java new file mode 100644 index 00000000..cf1c1525 --- /dev/null +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/enums/package-info.java @@ -0,0 +1 @@ +package hirs.attestationca.persist.enums; \ No newline at end of file diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/service/CertificateServiceImpl.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/service/CertificateServiceImpl.java new file mode 100644 index 00000000..31f0ac5e --- /dev/null +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/service/CertificateServiceImpl.java @@ -0,0 +1,20 @@ +package hirs.attestationca.persist.service; + +import hirs.attestationca.persist.entity.manager.CertificateRepository; +import hirs.attestationca.persist.entity.userdefined.Certificate; +import jakarta.persistence.EntityManager; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class CertificateServiceImpl { + @Autowired(required = false) + private EntityManager entityManager; + + @Autowired + private CertificateRepository repository; + + private void saveCertificate(Certificate certificate) { + repository.save(certificate); + } +} diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/service/DbServiceImpl.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/service/DbServiceImpl.java new file mode 100644 index 00000000..871c7403 --- /dev/null +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/service/DbServiceImpl.java @@ -0,0 +1,15 @@ +package hirs.attestationca.persist.service; + +public class DbServiceImpl { + /** + * The default maximum number of retries to attempt a database transaction. + */ + public static final int DEFAULT_MAX_RETRY_ATTEMPTS = 10; + /* + * The default number of milliseconds to wait before retrying a database transaction. + */ + private static final long DEFAULT_RETRY_WAIT_TIME_MS = 3000; + + // structure for retrying methods in the database +// private RetryTemplate retryTemplate; +} diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/service/DefaultService.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/service/DefaultService.java new file mode 100644 index 00000000..5da9fb2c --- /dev/null +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/service/DefaultService.java @@ -0,0 +1,4 @@ +package hirs.attestationca.persist.service; + +public interface DefaultService { +} diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/service/DeviceServiceImpl.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/service/DeviceServiceImpl.java new file mode 100644 index 00000000..705e934e --- /dev/null +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/service/DeviceServiceImpl.java @@ -0,0 +1,32 @@ +package hirs.attestationca.persist.service; + +import hirs.attestationca.persist.entity.manager.DeviceRepository; +import hirs.attestationca.persist.entity.userdefined.Device; +import jakarta.persistence.EntityManager; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * https://github.com/darrachequesne/spring-data-jpa-datatables + */ +@Service +public class DeviceServiceImpl { + + @Autowired(required = false) + private EntityManager entityManager; + @Autowired + private DeviceRepository deviceRepository; + + + public void saveDevice(Device device) { + this.deviceRepository.save(device); + } + + public void saveDevices(List devices) { + for (Device device : devices) { + this.deviceRepository.save(device); + } + } +} diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/service/ReferenceManifestServiceImpl.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/service/ReferenceManifestServiceImpl.java new file mode 100644 index 00000000..4ee3ad3a --- /dev/null +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/service/ReferenceManifestServiceImpl.java @@ -0,0 +1,70 @@ +package hirs.attestationca.persist.service; + +import hirs.attestationca.persist.entity.manager.ReferenceManifestRepository; +import hirs.attestationca.persist.entity.userdefined.ReferenceManifest; +import jakarta.persistence.EntityManager; +import lombok.extern.log4j.Log4j2; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.xml.sax.SAXException; + +import javax.xml.transform.stream.StreamSource; +import javax.xml.validation.Schema; +import javax.xml.validation.SchemaFactory; +import java.io.IOException; +import java.io.InputStream; + +@Log4j2 +@Service +public class ReferenceManifestServiceImpl { + + /** + * The variable that establishes a schema factory for xml processing. + */ + public static final SchemaFactory SCHEMA_FACTORY + = SchemaFactory.newInstance(ReferenceManifest.SCHEMA_LANGUAGE); + + @Autowired(required = false) + private EntityManager entityManager; + + @Autowired + private ReferenceManifestRepository repository; + + private static Schema schema; + + public ReferenceManifestServiceImpl() { + getSchemaObject(); + } + + /** + * This method sets the xml schema for processing RIMs. + * + * @return the schema + */ + public static final Schema getSchemaObject() { + if (schema == null) { + InputStream is = null; + try { + is = ReferenceManifest.class + .getClassLoader() + .getResourceAsStream(ReferenceManifest.SCHEMA_URL); + schema = SCHEMA_FACTORY.newSchema(new StreamSource(is)); + } catch (SAXException saxEx) { + log.error(String.format("Error setting schema for validation!%n%s", + saxEx.getMessage())); + } finally { + if (is != null) { + try { + is.close(); + } catch (IOException ioEx) { + log.error(String.format("Error closing input stream%n%s", + ioEx.getMessage())); + } + } else { + log.error("Input stream variable is null"); + } + } + } + return schema; + } +} diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/service/SettingsServiceImpl.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/service/SettingsServiceImpl.java new file mode 100644 index 00000000..e3e9e553 --- /dev/null +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/service/SettingsServiceImpl.java @@ -0,0 +1,41 @@ +package hirs.attestationca.persist.service; + +import hirs.attestationca.persist.entity.manager.SettingsRepository; +import hirs.attestationca.persist.entity.userdefined.SupplyChainSettings; +import jakarta.persistence.EntityManager; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class SettingsServiceImpl { + + @Autowired(required = false) + private EntityManager entityManager; + + @Autowired + private SettingsRepository repository; + + public SupplyChainSettings updateSettings(SupplyChainSettings settings) { + SupplyChainSettings existing = repository.findByName(settings.getName()); + + if (existing != null) { + settings.setId(existing.getId()); + } + return repository.save(settings); + } + + public void saveSettings(SupplyChainSettings settings) { + repository.save(settings); + } + + public SupplyChainSettings getByName(String name) { + if (name == null) { + return null; + } + return repository.findByName(name); + } + +// public Policy getDefaultPolicy(Appraiser appraiser) { +// return repository.findByAppraiser(appraiser); +// } +} diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/type/InetAddressType.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/type/InetAddressType.java new file mode 100644 index 00000000..27fc35c4 --- /dev/null +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/type/InetAddressType.java @@ -0,0 +1,190 @@ +package hirs.attestationca.persist.type; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import org.hibernate.HibernateException; +import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.usertype.UserType; + +import java.io.Serializable; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Types; +import java.util.Objects; + +/** + * This is a class for persisting InetAddress objects via + * Hibernate. This class provides the mapping from InetAddress to + * Hibernate commands to JDBC. + */ +@NoArgsConstructor(access = AccessLevel.PUBLIC) +public final class InetAddressType implements UserType { + + /** + * Returns varchar type. + * + * @return varchar type + */ + @Override + public int getSqlType() { + return Types.VARCHAR; + } + + /** + * Returns the InetAddress class. + * + * @return InetAddress class + */ + @Override + public Class returnedClass() { + return InetAddress.class; + } + + /** + * Compares x and y using {@link Objects#equals(Object, Object)}. + * + * @param x x + * @param y y + * @return value from equals call + */ + @Override + public boolean equals(final Object x, final Object y) { + return Objects.equals(x, y); + } + + /** + * Returns the hash code of x, which will be the same as from + * InetAddress. + * + * @param x x + * @return hash value of x + */ + @Override + public int hashCode(final Object x) { + assert x != null; + return x.hashCode(); + } + + /** + * Converts the IP address that is stored as a String and + * converts it to an InetAddress. + * + * @param rs + * result set + * @param index + * column names + * @param session + * session + * @param owner + * owner + * @return InetAddress of String + * @throws HibernateException + * if unable to convert the String to an InetAddress + * @throws SQLException + * if unable to retrieve the String from the result set + */ + @Override + public Object nullSafeGet(final ResultSet rs, final int index, + final SharedSessionContractImplementor session, final Object owner) + throws HibernateException, SQLException { + + final String ip = rs.getString(index); + if (ip == null) { + return null; + } + try { + return InetAddress.getByName(ip); + } catch (UnknownHostException e) { + final String msg = String.format("unable to convert ip address: %s", ip); + throw new HibernateException(msg, e); + } + } + + /** + * Converts the InetAddress value to a + * String and stores it in the database. + * + * @param st prepared statement + * @param value InetAddress + * @param index index + * @param session session + * @throws SQLException if unable to set the value in the result set + */ + @Override + public void nullSafeSet(final PreparedStatement st, final Object value, + final int index, final SharedSessionContractImplementor session) + throws SQLException { + if (value == null) { + st.setString(index, null); + } else { + final InetAddress address = (InetAddress) value; + final String ip = address.getHostAddress(); + st.setString(index, ip); + } + } + + /** + * Returns value since InetAddress is immutable. + * + * @param value value + * @return value + * @throws HibernateException will never be thrown + */ + @Override + public Object deepCopy(final Object value) throws HibernateException { + return value; + } + + /** + * Returns false because InetAddress is immutable. + * + * @return false + */ + @Override + public boolean isMutable() { + return false; + } + + /** + * Returns value because InetAddress is + * immutable. + * + * @param value value + * @return value + */ + @Override + public Serializable disassemble(final Object value) { + return (Serializable) value; + } + + /** + * Returns cached because InetAddress is + * immutable. + * + * @param cached cached + * @param owner owner + * @return cached + */ + @Override + public Object assemble(final Serializable cached, final Object owner) { + return cached; + } + + /** + * Returns the original because InetAddress is + * immutable. + * + * @param original original + * @param target target + * @param owner owner + * @return original + */ + @Override + public Object replace(final Object original, final Object target, + final Object owner) { + return original; + } +} diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/type/X509CertificateType.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/type/X509CertificateType.java new file mode 100644 index 00000000..9836a682 --- /dev/null +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/type/X509CertificateType.java @@ -0,0 +1,203 @@ +package hirs.attestationca.persist.type; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import org.hibernate.HibernateException; +import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.usertype.UserType; + +import javax.sql.rowset.serial.SerialBlob; +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.io.Serializable; +import java.security.cert.Certificate; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.sql.Blob; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Types; +import java.util.Objects; + +/** + * This is a class for persisting X509Certificate objects via + * Hibernate. This class provides the mapping from X509Certificate + * to Hibernate commands to JDBC. + */ +@NoArgsConstructor(access= AccessLevel.PUBLIC) +public final class X509CertificateType implements UserType { + + @Override + public int getSqlType() { + return Types.BLOB; + } + + /** + * Returns the X509Certificate class. + * + * @return X509Certificate class + */ + @Override + public Class returnedClass() { + return X509Certificate.class; + } + + /** + * Compares x and y using {@link Objects#equals(Object, Object)}. + * + * @param x x + * @param y y + * @return value from equals call + */ + @Override + public boolean equals(final Object x, final Object y) { + return Objects.equals(x, y); + } + + /** + * Returns the hash code of x, which will be the same as from + * X509Certificate. + * + * @param x x + * @return hash value of x + */ + @Override + public int hashCode(final Object x) { + assert x != null; + return x.hashCode(); + } + + /** + * Converts the X509Certificate that is stored as a String and + * converts it to an X509Certificate. + * + * @param rs + * result set + * @param names + * column names + * @param session + * session + * @param owner + * owner + * @return X509Certificate of String + * @throws HibernateException + * if unable to convert the String to an X509Certificate + * @throws SQLException + * if unable to retrieve the String from the result set + */ + @Override + public Object nullSafeGet(final ResultSet rs, final int names, + final SharedSessionContractImplementor session, final Object owner) + throws HibernateException, SQLException { + final Blob cert = rs.getBlob(names); + if (cert == null) { + return null; + } + try { + InputStream inputStream = new ByteArrayInputStream( + cert.getBytes(1, (int) cert.length())); + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + return cf.generateCertificate(inputStream); + } catch (CertificateException e) { + final String msg = String.format( + "unable to convert certificate: %s", cert); + throw new HibernateException(msg, e); + } + } + + /** + * Converts the X509Certificate value to a + * String and stores it in the database. + * + * @param st prepared statement + * @param value X509Certificate + * @param index index + * @param session session + * @throws SQLException if unable to set the value in the result set + */ + @Override + public void nullSafeSet(final PreparedStatement st, final Object value, + final int index, final SharedSessionContractImplementor session) + throws SQLException { + if (value == null) { + st.setString(index, null); + } else { + try { + Blob blob = + new SerialBlob(((Certificate) value).getEncoded()); + st.setBlob(index, blob); + } catch (Exception e) { + final String msg = + String.format("unable to convert certificate: %s", + value.toString()); + throw new HibernateException(msg, e); + } + } + + } + + /** + * Returns value since X509Certificate is + * immutable. + * + * @param value value + * @return value + * @throws HibernateException will never be thrown + */ + @Override + public Object deepCopy(final Object value) throws HibernateException { + return value; + } + + /** + * Returns false because X509Certificate is immutable. + * + * @return false + */ + @Override + public boolean isMutable() { + return false; + } + + /** + * Returns value because X509Certificate is + * immutable. + * + * @param value value + * @return value + */ + @Override + public Serializable disassemble(final Object value) { + return (Serializable) value; + } + + /** + * Returns cached because X509Certificate is + * immutable. + * + * @param cached cached + * @param owner owner + * @return cached + */ + @Override + public Object assemble(final Serializable cached, final Object owner) { + return cached; + } + + /** + * Returns the original because X509Certificate is + * immutable. + * + * @param original original + * @param target target + * @param owner owner + * @return original + */ + @Override + public Object replace(final Object original, final Object target, + final Object owner) { + return original; + } +} diff --git a/HIRS_AttestationCA/src/main/resources/component-class.json b/HIRS_AttestationCA/src/main/resources/component-class.json new file mode 100644 index 00000000..c4b81213 --- /dev/null +++ b/HIRS_AttestationCA/src/main/resources/component-class.json @@ -0,0 +1,477 @@ +{ + "TCG": { + "Processors": { + "ID": "0x00010000", + "Types": { + "0x00000002": "CPU", + "0x00000003": "Math Processor", + "0x00000004": "DSP Processor", + "0x00000005": "Video Processor", + "0x00000006": "GPU" + } + }, + "Containers": { + "ID": "0x00020000", + "Types": { + "0x00000002": "Desktop", + "0x00000003": "Low Profile Desktop", + "0x00000004": "Pizza Box", + "0x00000005": "Mini Tower", + "0x00000006": "Tower", + "0x00000007": "Portable", + "0x00000008": "Laptop", + "0x00000009": "Notebook", + "0x0000000A": "Hand Held", + "0x0000000B": "Docking Station", + "0x0000000C": "All in One", + "0x0000000D": "Sub Notebook", + "0x0000000E": "Space-saving", + "0x0000000F": "Lunch Box", + "0x00000010": "Main Server Chassis", + "0x00000011": "Expansion Chassis", + "0x00000012": "Sub Chassis", + "0x00000013": "Bus Expansion Chassis", + "0x00000014": "Peripheral Chassis", + "0x00000015": "RAID Chassis", + "0x00000016": "Rack Mount Chassis", + "0x00000017": "Sealed-case PC", + "0x00000018": "Multi-system Chassis", + "0x00000019": "Compact PCI", + "0x0000001A": "Advanced TCA", + "0x0000001B": "Blade", + "0x0000001C": "Blade Enclosure", + "0x0000001D": "Tablet", + "0x0000001E": "Convertible", + "0x0000001F": "Detachable", + "0x00000020": "IoT Gateway", + "0x00000021": "Embedded PC", + "0x00000022": "MiniPC", + "0x00000023": "Stick PC", + "0x00000024": "1U Rack Mount Chassis", + "0x00000025": "2U Rack Mount Chassis", + "0x00000026": "3U Rack Mount Chassis", + "0x00000027": "4U Rack Mount Chassis", + "0x00000028": "5U Rack Mount Chassis", + "0x00000029": "6U Rack Mount Chassis", + "0x0000002A": "7U Rack Mount Chassis", + "0x0000002B": "8U Rack Mount Chassis" + } + }, + "IC Boards": { + "ID": "0x00030000", + "Types": { + "0x00000002": "Daughter Board", + "0x00000003": "Motherboard", + "0x00000004": "Riser Card" + } + }, + "Modules": { + "ID": "0x00040000", + "Types": { + "0x00000002": "SAS Bridgeboard", + "0x00000003": "Processor Module", + "0x00000004": "I/O Module", + "0x00000005": "Memory Module", + "0x00000006": "Power Module", + "0x00000007": "Processor/Memory Module", + "0x00000008": "Processor/IO Module", + "0x00000009": "TPM" + } + }, + "Controllers": { + "ID": "0x00050000", + "Types": { + "0x00000002": "Video Controller", + "0x00000003": "SCSI Controller", + "0x00000004": "Ethernet Controller", + "0x00000005": "Token Ring Controller", + "0x00000006": "Audio/Sound Controller", + "0x00000007": "PATA Controller", + "0x00000008": "SATA Controller", + "0x00000009": "SAS Controller", + "0x0000000A": "LED Display Controller", + "0x0000000B": "RAID Controller", + "0x0000000C": "Remote Access Controller", + "0x0000000E": "USB Controller", + "0x0000000F": "Multi-function Storage Controller", + "0x00000010": "Multi-function Network Controller", + "0x00000011": "Smart IO Controller" + } + }, + "Memory": { + "ID": "0x00060000", + "Types": { + "0x00000002": "Port Controller", + "0x00000003": "Baseboard Management Controller", + "0x00000004": "DRAM Memory", + "0x00000005": "EDRAM Memory", + "0x00000006": "VRAM Memory", + "0x00000007": "SRAM Memory", + "0x00000008": "RAM Memory", + "0x00000009": "ROM Memory", + "0x0000000A": "FLASH Memory", + "0x0000000B": "EEPROM Memory", + "0x0000000C": "FEPROM Memory", + "0x0000000D": "EPROM Memory", + "0x0000000E": "CDRAM Memory", + "0x0000000F": "3DRAM Memory", + "0x00000010": "SDRAM Memory", + "0x00000011": "SGRAM Memory", + "0x00000012": "RDRAM Memory", + "0x00000013": "DDR Memory", + "0x00000014": "DDR2 Memory", + "0x00000015": "DDR3 Memory", + "0x00000016": "DDR4 Memory", + "0x00000017": "LPDDR Memory", + "0x00000018": "LPDDR2 Memory", + "0x00000019": "LPDDR3 Memory", + "0x0000001A": "LPDDR4 Memory", + "0x0000001B": "NVRAM Memory", + "0x0000001C": "3D Xpoint Memory" + } + }, + "Storage": { + "ID": "0x00070000", + "Types": { + "0x00000002": "Storage Drive", + "0x00000003": "SSD Drive", + "0x00000004": "M.2 Drive", + "0x00000005": "HDD Drive" + } + }, + "Media Drives": { + "ID": "0x00080000", + "Types": { + "0x00000002": "Floppy Drive", + "0x00000003": "Tape Drive", + "0x00000004": "PCIe Drive", + "0x00000005": "CD Drive", + "0x00000006": "DVD Drive", + "0x00000007": "Blu-Ray Drive" + } + }, + "Network Adapters": { + "ID": "0x00090000", + "Types": { + "0x00000002": "Ethernet Adapter", + "0x00000003": "WiFi Adapter", + "0x00000004": "Bluetooh Adapter", + "0x00000005": "Cellular Adapter", + "0x00000006": "Zigbee Adapter", + "0x00000007": "3G Cellular Adapter", + "0x00000008": "4G Cellular Adapter", + "0x00000009": "5G Cellular Adapter", + "0x0000000A": "Network Switch", + "0x0000000B": "Network Router" + } + }, + "Energy Object": { + "ID": "0x000A0000", + "Types": { + "0x00000002": "Power Supply", + "0x00000003": "Battery", + "0x00000004": "Coin Battery", + "0x00000005": "Capacitor Battery" + } + }, + "Sensors": { + "ID": "0x000B0000", + "Types": { + "0x00000002": "Optical Sensor", + "0x00000003": "Temperature Sensor", + "0x00000004": "Proximity Sensor", + "0x00000005": "IR Sensor", + "0x00000006": "Chemical Sensor", + "0x00000007": "Motion Detection Sensor", + "0x00000008": "Level Sensor", + "0x00000009": "Gyroscopic Sensor", + "0x0000000A": "Humidity Sensor", + "0x0000000B": "Accelerometer Sensor" + } + }, + "Display Devices": { + "ID": "0x000C0000", + "Types": { + "0x00000002": "LCD Display Panel", + "0x00000003": "LED Display Panel", + "0x00000004": "OLED Display Panel", + "0x00000005": "CRT Display Panel" + } + }, + "Cooling": { + "ID": "0x000D0000", + "Types": { + "0x00000002": "Thermal Assembly", + "0x00000003": "Fan", + "0x00000004": "Chassis Fan", + "0x00000005": "Socket Fan", + "0x00000006": "Heatsink", + "0x00000007": "Liquid Cooling" + } + }, + "Input Devices": { + "ID": "0x000E0000", + "Types": { + "0x00000002": "Mouse", + "0x00000003": "Track Ball", + "0x00000004": "Track Point", + "0x00000005": "Glide Point", + "0x00000006": "Touch Pad", + "0x00000007": "Touch Screen", + "0x00000008": "Camera", + "0x00000009": "Fingerprint Reader", + "0x0000000A": "Keyboard", + "0x0000000B": "Smartcard Reader", + "0x0000000C": "Biometric Reader", + "0x0000000D": "Joystick", + "0x0000000E": "Gaming Controller", + "0x0000000F": "IR Camera", + "0x00000010": "Facial Recognition Camera", + "0x00000011": "Scanner" + } + }, + "Slots": { + "ID": "0x000F0000", + "Types": { + "0x00000002": "Socket", + "0x00000003": "ISA Slot", + "0x00000004": "PCI Slot", + "0x00000005": "AGP Slot", + "0x00000006": "PCI-X Slot", + "0x00000007": "M.2 Slot", + "0x00000008": "MXM Slot", + "0x00000009": "PCI Express Slot", + "0x0000000A": "PCI Express Mini", + "0x0000000B": "PC-98 Slot", + "0x0000000C": "Memory Slot" + } + }, + "Ports": { + "ID": "0x00100000", + "Types": { + "0x00000002": "Parallel Port", + "0x00000003": "Serial Port", + "0x00000004": "SCSI Port", + "0x00000005": "MIDI Port", + "0x00000006": "USB Port", + "0x00000007": "Firewire Port", + "0x00000008": "PCMCIA Port", + "0x00000009": "ATA Port", + "0x0000000A": "SATA Port", + "0x0000000B": "SAS Port", + "0x0000000C": "Optical Port", + "0x0000000D": "DisplayPort", + "0x0000000E": "Mini DisplayPort", + "0x0000000F": "HDMI Port", + "0x00000010": "Mini HDMI Port", + "0x00000011": "Micro HDMI Port", + "0x00000012": "Thunderbolt Port", + "0x00000013": "VGA Port", + "0x00000014": "Mini VGA Port", + "0x00000015": "DVI Port", + "0x00000016": "DVI-I Port", + "0x00000017": "DVI-D Port", + "0x00000018": "DVI-A Port", + "0x00000019": "Mini DVI Port", + "0x0000001A": "Micro DVI Port", + "0x0000001B": "Ethernet Port", + "0x0000001C": "ADB Port", + "0x0000001D": "Mac Serial Port", + "0x0000001E": "PS/2 Port", + "0x0000001F": "Surround Sound Port", + "0x00000020": "Stereo Port", + "0x00000021": "Dolby 5.1 Port", + "0x00000022": "Dolby 7.1 Port", + "0x00000023": "Dolby 7.2 Port", + "0x00000024": "Line In Port", + "0x00000025": "Microphone Port", + "0x00000026": "Speaker Port", + "0x00000027": "Digital Audio Port", + "0x00000028": "TOSLINK Port" + } + }, + "Discrete Component": { + "ID": "0x00110000", + "Types": { + "0x00000002": "Capacitor", + "0x00000003": "Resistor", + "0x00000004": "Inductor", + "0x00000005": "Diode", + "0x00000006": "Crystal Oscilator", + "0x00000007": "Logic Gate", + "0x00000008": "Ferrite Beads", + "0x00000009": "Transistor", + "0x0000000A": "Fuse", + "0x0000000B": "Voltage Regulator", + "0x0000000C": "DC/DC Converter", + "0x0000000D": "Switching Regulator", + "0x0000000E": "Power Switch" + } + }, + "Cabling": { + "ID": "0x00120000", + "Types": { + "0x00000002": "AC Adapter", + "0x00000003": "Power Cord", + "0x00000004": "Serial ATA Cable", + "0x00000005": "Serial ATA Power Cable", + "0x00000006": "Drive Cable", + "0x00000007": "Power Supply Cable", + "0x00000008": "IDE Cable", + "0x00000009": "Molex Cable", + "0x0000000A": "Ribbon Cable", + "0x0000000B": "PCI Express Cable" + } + }, + "Firmware": { + "ID": "0x00130000", + "Types": { + "0x00000002": "UEFI", + "0x00000003": "System BIOS", + "0x00000004": "Drive BIOS", + "0x00000005": "Bootloader", + "0x00000006": "System Management Module" + } + } + }, + "SMBIOS": { + "BIOS": { + "ID": "0x00000000", + "Types": { + "0x00000001": "Other", + "0x00000002": "Unknown" + } + }, + "System": { + "ID": "0x00010000", + "Types": { + "0x00000001": "Other", + "0x00000002": "Unknown" + } + }, + "Baseboard": { + "ID": "0x00020000", + "Types": { + "0x00000001": "Other", + "0x00000002": "Unknown", + "0x00000003": "Server Blade", + "0x00000004": "Connectivity Switch", + "0x00000005": "System Management Module", + "0x00000006": "Processor Module", + "0x00000007": "I/O Module", + "0x00000008": "Memory Module", + "0x00000009": "Daughter board", + "0x0000000A": "Motherboard (includes processor, memory, and I/O)", + "0x0000000B": "Processor/Memory Module", + "0x0000000C": "Processor/IO Module", + "0x0000000D": "Interconnect board" + } + }, + "Chassis": { + "ID": "0x00030000", + "Types": { + "0x00000001": "Other", + "0x00000002": "Unknown", + "0x00000003": "Desktop", + "0x00000004": "Low Profile Desktop", + "0x00000005": "Pizza Box", + "0x00000006": "Mini Tower", + "0x00000007": "Tower", + "0x00000008": "Portable", + "0x00000009": "Laptop", + "0x0000000A": "Notebook", + "0x0000000B": "Hand Held", + "0x0000000C": "Docking Station", + "0x0000000D": "All in One", + "0x0000000E": "Sub Notebook", + "0x0000000F": "Space-saving", + "0x00000010": "Lunch Box", + "0x00000011": "Main Server Chassis", + "0x00000012": "Expansion Chassis", + "0x00000013": "SubChassis", + "0x00000014": "Bus Expansion Chassis", + "0x00000015": "Peripheral Chassis", + "0x00000016": "RAID Chassis", + "0x00000017": "Rack Mount Chassis", + "0x00000018": "Sealed-case PC", + "0x00000019": "Multi-system chassis", + "0x0000001A": "Compact PCI", + "0x0000001B": "Advanced TCA", + "0x0000001C": "Blade", + "0x0000001D": "Blade Enclosure", + "0x0000001E": "Tablet", + "0x0000001F": "Convertible", + "0x00000020": "Detachable", + "0x00000021": "IoT Gateway", + "0x00000022": "Embedded PC", + "0x00000023": "Mini PC", + "0x00000024": "Stick PC" + } + }, + "Processor": { + "ID": "0x00040000", + "Types": { + "0x00000001": "Other", + "0x00000002": "Unknown", + "0x00000003": "Central Processor", + "0x00000004": "Math Processor", + "0x00000005": "DSP Processor", + "0x00000006": "Video Processor" + } + }, + "RAM": { + "ID": "0x00110000", + "Types": { + "0x00000001": "Other", + "0x00000002": "Unknown", + "0x00000003": "DRAM", + "0x00000004": "EDRAM", + "0x00000005": "VRAM", + "0x00000006": "SRAM", + "0x00000007": "RAM", + "0x00000008": "ROM", + "0x00000009": "FLASH", + "0x0000000A": "EEPROM", + "0x0000000B": "FEPROM", + "0x0000000C": "EPROM", + "0x0000000D": "CDRAM", + "0x0000000E": "3DRAM", + "0x0000000F": "SDRAM", + "0x00000010": "SGRAM", + "0x00000011": "RDRAM", + "0x00000012": "DDR", + "0x00000013": "DDR2", + "0x00000014": "DDR2 FB-DIMM", + "0x00000015": "Reserved", + "0x00000016": "Reserved", + "0x00000017": "Reserved", + "0x00000018": "DDR3", + "0x00000019": "FBD2", + "0x0000001A": "DDR4", + "0x0000001B": "LPDDR", + "0x0000001C": "LPDDR2", + "0x0000001D": "LPDDR3", + "0x0000001E": "LPDDR4", + "0x0000001F": "Logical non-volatile device", + "0x00000020": "HBM (High Bandwidth Memory)", + "0x00000021": "HBM2 (High Bandwidth Memory Generation 2)", + "0x00000022": "DDR5", + "0x00000023": "LPDDR5" + } + }, + "Power Supply": { + "ID": "0x00270000", + "Types": { + "0x00000001": "Other", + "0x00000002": "Unknown" + } + }, + "TPM": { + "ID": "0x002B0000", + "Types": { + "0x00000001": "Other", + "0x00000002": "Unknown" + } + } + } +} \ No newline at end of file diff --git a/HIRS_AttestationCA/src/main/resources/vendor-table.json b/HIRS_AttestationCA/src/main/resources/vendor-table.json new file mode 100644 index 00000000..d411dd50 --- /dev/null +++ b/HIRS_AttestationCA/src/main/resources/vendor-table.json @@ -0,0 +1,233 @@ +{ + "VendorTable": { + "_comment_1": "UUIDS listed in the UEFI Specification", + "eb9d2d30-2d88-11d3-9a16-0090273fc14d": "ACPI_TABLE_GUID", + "eb9d2d32-2d88-11d3-9a16-0090273fc14d": "SAL_SYSTEM_TABLE_GUID", + "eb9d2d31-2d88-11d3-9a16-0090273fc14d": "SMBIOS_TABLE_GUID", + "f2fd1544-9794-4a2c-992e-e5bbcf20e394": "SMBIOS3_TABLE_GUID", + "eb9d2d2f-2d88-11d3-9a16-0090273fc14d": "MPS_TABLE_GUID", + "8868e871-e4f1-11d3-bc22-0080c73c8881": "EFI_ACPI_TABLE_GUID", + "87367f87-1119-41ce-aaec-8be01101f558": "EFI_JSON_CONFIG_DATA_TABLE_GUID", + "35e7a725-8dd2-4cac-8011-33cda8109056": "EFI_JSON_CAPSULE_DATA_TABLE_GUID", + "dbc461c3-b3de-422a-b9b4-9886fd49a1e5": "EFI_JSON_CAPSULE_RESULT_TABLE_GUID", + "77ab535a-45fc-624b-5560-f7b281d1f96e": "EFI_VIRTUAL_DISK_GUID", + "3d5abd30-4175-87Ce-6d64-d2ADe523C4bb": "EFI_VIRTUAL_CD_GUID", + "5Cea02c9-4d07-69d3-269f-4496Fbe096f9": "EFI_PERSISTENT_VIRTUAL_DISK_GUID", + "08018188-42cd-bb48-100f-5387D53ded3d": "EFI_PERSISTENT_VIRTUAL_CD_GUID", + "_comment_2": "DXE GUIds from https://github.com/linuxboot/linuxboot/blob/master/boards/qemu/image-files.txt", + "fc510ee7-ffdc-11d4-bd41-0080c73c8881": "DXE Apriori-FVRECOVERY", + "1b45cc0a-156a-428a-62af-49864da0e6e6": "PEI Apriori file name", + "80cf7257-87ab-47f9-a3fe-d50b76d89541": "PcdDxe", + "b601f8c4-43b7-4784-95b1-f4226cb40cee": "RuntimeDxe", + "f80697e9-7fd6-4665-8646-88e33ef71dfc": "SecurityStubDxe", + "1a1e4886-9517-440e-9fde-3be44cee2136": "CpuDxe", + "11a6edf6-a9be-426d-a6cc-b22fe51d9224": "PciHotPlugInitDxe", + "128fb770-5e79-4176-9e51-9bb268a17dd1": "PciHostBridgeDxe", + "93b80004-9fb3-11d4-9a3a-0090273fc14d": "PCI Bus Driver - PciBusDxe", + "9b680fce-ad6b-4f3a-b60b-f59899003443": "DevicePathDxe", + "f9d88642-0737-49bc-81b5-6889cd57d9ea": "SmbiosDxe", + "4110465d-5ff3-4f4b-b580-24ed0d06747a": "SmbiosPlatformDxe", + "9622e42c-8e38-4a08-9e8f-54f784652f6b": "AcpiTableDxe", + "49970331-e3fa-4637-9abc-3b7868676970": "AcpiPlatform", + "7e374e25-8e01-4fee-87f2-390c23c606cd": "ACPI data", + "bdce85bb-fbaa-4f4e-9264-501a2c249581": "S3SaveStateDxe", + "d9dcc5df-4007-435e-9098-8970935504b2": "PlatformDxe", + "8657015b-ea43-440d-949a-af3be365c0fc": "IoMmuDxe", + "cbd2e4d5-7068-4ff5-b462-9822b4ad8d60": "VariableRuntimeDxe", + "_comment_3": "PIWG Dxe driver Files (FvFile)from https://bugs.launchpad.net/ubuntu/+source/edk2/+bug/1272444", + "70d57d67-7f05-494d-a014-b75d7345b700": "Storage Security Command Driver", + "3acc966d-8e33-45c6-b4fe-62724bcd15a9": "AHCI Bus Driver", + "67bbc344-84bc-4e5c-b4df-f5e4a00e1f3a": "Host Controller Driver", + "86edaae5-073c-4c89-b949-8984ac8a55f3": "MMC/SD Media Device Driver", + "9e863906-a40f-4875-977F-5b93ff237fc6": "Serial Terminal Driver", + "a6cc6bc8-2ada-46C3-bba4-e99672CC9530": "PCI Serial Driver", + "69fd8e47-a161-4550-b01a-5594ceb2b2b2": "PCI IDE/ATAPI Bus Driver", + "51ccf399-4fdf-4e55-a45b-e123f84d456a": "Platform Console Management Driver", + "6b38f7b4-ad98-40e9-9093-aca2b5a253c4": "Generic Disk I/O Driver", + "2d2e62cf-9ecf-43b7-8219-94e7fC713dfe": "Usb Keyboard Driver", + "9fb4b4a7-42C0-4bcd-8540-9bcc6711f83e": "Usb Mass Storage Driver", + "e3752948-b9a1-4770-90c4-df41c38986be": "QEMU Video Driver", + "240612B7-a063-11d4-9a3a-0090273fc14d": "Usb Bus Driver", + "bdfe430e-8F2a-4db0-9991-6f856594777e": "Usb Ehci Driver", + "2fb92efa-2ee0-4bae-9eB6-7464125E1EF7": "Usb Ehci Driver", + "a92cdb4b-82f1-4e0b-a516-8a655d371524": "Virtio Network Driver", + "4579b72d-7ec4-4dd4-8486-083c86b182a7": "iSCSI Driver", + "3b1deaB5-c75d-442e-9238-8e2ffb62b0bb": "UEFI PXE Base Code Driver", + "6b6963ab-906d-4a65-a7ca-bd40e5d6af2b": "UDP Network Service Driver", + "6d6963ab-906d-4a65-a7ca-bd40e5d6af4d": "Tcp Network Service Driver", + "dc3641b8-2fa8-4ed3-bc1f-f9962a03454b": "MTFTP4 Network Service Driver", + "9fb1a1f3-3b71-4324-b39a-745cbb015fff": "IP4 Network Service Driver", + "26841bde-920a-4e7a-9Fbe-637f477143a6": "IP4 CONFIG Network Service Driver", + "94734718-0bbc-47fb-96a5-ee7a5ae6a2ad": "DHCP Protocol Driver", + "529d3f93-e8e9-4e73-b1e1-bdf6a9d50113": "ARP Network Service Driver", + "e4f61863-fe2c-4b56-a8d4-08519bc439df": "VLAN Configuration Driver", + "a2f436ea-a127-4ef8-957c-8048606ff670": "Simple Network Protocol Driver", + "961578fe-b6b7-44c3-af35-6bc705cd2b1f": "FAT File System Driver", + "0abd8284-6da3-4616-971a-83a5148067ba": "ISA Floppy Driver", + "3dc82376-637b-40a6-a8fc-a565417f2c38": "PS/2 Keyboard Driver", + "93b80003-9fb3-11d4-9a3a-0090273fc14d": "ISA Serial Driver", + "240612b5-a063-11d4-9a3a-0090273fc14a": "ISA Bus Driver", + "99549f44-49bb-4820-b9d2-901329412d67": "IDE Controller Init Driver", + "0a66e322-3740-4cce-ad62-bd172cecca35": "Scsi Disk Driver", + "1fa1f39e-feff-4aae-bd7b-38a070a3b609": "Partition Driver", + "9e863906-a40f-4875-977f-5b93ff237fc6": "Serial Terminal Driver", + "cccb0c28-4b24-11d5-9a5a-0090273fc14d": "Graphics Console Driver", + "408edcec-cf6d-477c-a5a8-b4844e3de281": "Console Splitter Driver", + "fab5d4f4-83c0-4aaf-8480-442d11df6cea": "Virtio SCSI Host Driver", + "11d92dfb-3Ca9-4f93-ba2e-4780ed3e03b5": "Virtio Block Driver", + "33cb97af-6c33-4c42-986b-07581fa366d4": "Block MMIO to Block IO Driver", + "_comment_4": "PIWG Volumes (Fv)", + "a881d567-6cb0-4eee-8435-2e72d33e45B5": "PIWG Default Volume", + "_comment_5": "UEFI UUIDS for Certificates", + "3c5766e8-269c-4e34-aa14-ed776e85b3b6": "EFI_CERT_RSA2048_GUID", + "e2b36190-879b-4a3d-ad8d-f2e7bba32784": "EFI_CERT_RSA2048_SHA256_GUID", + "c1c41626-504c-4092-aca9-41f936934328": "EFI_CERT_SHA256_GUID", + "826ca512-cf10-4ac9-b187-be01496631bd": "EFI_CERT_SHA1_GUID", + "67f8444f-8743-48f1-a328-1eaab8736080": "EFI_CERT_RSA2048_SHA1_GUID", + "a5c059a1-94e4-4aa7-87b5-ab155c2bf072": "EFI_CERT_X509_GUID", + "0b6e5233-a65c-44c9-9407-d9ab83bfc8bd": "EFI_CERT_SHA224_GUID", + "ff3e5307-9fd0-48c9-85f1-8ad56c701e01": "EFI_CERT_SHA384_GUID", + "093e0fae-a6c4-4f50-9f1b-d41e2b89c19a": "EFI_CERT_SHA512_GUID", + "3bd2a492-96c0-4079-b420-fcf98ef103ed": "EFI_CERT_X509_SHA256_GUID", + "7076876e-80c2-4ee6-aad2-28b349a6865b": "EFI_CERT_X509_SHA384_GUID", + "446dbf63-2502-4cda-bcfa-2465d2b0fe9d": "EFI_CERT_X509_SHA512_GUID", + "a7717414-c616-4977-9420-844712a735bf": "EFI_CERT_TYPE_RSA2048_SHA256_GUID", + "_comment_6": "UEFI defined variables", + "452e8ced-dfff-4b8c-ae01-5118862e682c": "EFI_CERT_EXTERNAL_MANAGEMENT_GUID", + "d719b2cb-3d3a-4596-a3bc-dad00e67656f": "EFI_IMAGE_SECURITY_DATABASE_GUID", + "4aafd29d-68df-49ee-8aa9-347d375665a7": "EFI_CERT_TYPE_PKCS7_GUID", + "c12a7328-f81f-11d2-ba4b-00a0c93ec93b": "EFI System Partition", + "024DEE41-33E7-11D3-9D69-0008C781F39F": "Partition containing a legacy MBR", + "_comment_7": "RHBoot UEFI Application UUIDs From listed in RHBoot (RHShim) https://github.com/rhboot/efivar/blob/master/src/guids.txt", + "0abba7dc-e516-4167-bbf5-4d9d1c739416": "fwupdate:", + "3b8c8162-188c-46a4-aec9-be43f1d65697": "ux_capsule", + "605dab50-e046-4300-abb6-3dd810dd8b23": "RH_Shim", + "8be4df61-93ca-11d2-aa0d-00e098032b8c": "EFI_Global_Variable", + "91376aff-cba6-42be-949d-06fde81128e8": "GRUB", + "_comment_8": "Partition Table GUIDs", + "0fc63daf-8483-4772-8e79-3d69d8477de4": "Linux filesystem data", + "e6d6d379-f507-44c2-a23c-238f2a3df928": "Logical Volume Manager (LVM) partition", + "4f68bce3-e8cd-4db1-96e7-fbcaf984b709": "Root partition (x86-64)", + "a19d880f-05fc-4d3b-a006-743f0f84911e": "RAID partition", + "933ac7e1-2eb4-4f13-b844-0e14e2aef915": "/home partition[ (x86-64)", + "ebd0a0a2-b9e5-4433-87c0-68b6b72699c7": "GPT Basic data partition", + "_comment_9": "RHBoot Lenovo specific UUIDS", + "3cc24e96-22c7-41d8-8863-8e39dcdcc2cf": "lenovo", + "82988420-7467-4490-9059-feb448dd1963": "lenovo_me_config", + "f7e615b-0d45-4f80-88dc-26b234958560": "lenovo_diag", + "665d3f60-ad3e-4cad-8e26-db46eee9f1b5": "lenovo_rescue", + "721c8b66-426c-4e86-8e99-3457c46ab0b9": "lenovo_setup", + "f46ee6f4-4785-43a3-923d-7f786c3c8479": "lenovo_startup_interrupt", + "126a762d-5758-4fca-8531-201a7f57f850": "lenovo_boot_menu", + "a7d8d9a6-6ab0-4aeb-ad9d-163e59a7a380": "lenovo_diag_splash", + "_comment_10": "Company UUIDs (From Internet searches)", + "77fa9abd-0359-4d32-bd60-28f4e78f784b": "Microsoft Inc.", + "f5a96b31-dba0-4faa-a42a-7a0c9832768e": "HPE Inc.", + "2879c886-57ee-45cc-b126-f92f24f906b9": "SUSE Certificate", + "70564dce-9afc-4ee3-85fc-949649d7e45c": "Dell Inc.", + "_comment_11": "Intel GUIDS", + "bfcc0833-2125-42d1-8c6d-13821e23c078": "Intel(R) Desktop Boards", + "80b3ad5b-9880-4af9-a645-e56a68be89de": "Intel(R) CISD FW Update", + "_comment_12": "Microsoft GUIDS", + "e3c9e316-0b5c-4db8-817d-f92df00215ae": "Microsoft Reserved Partition (MSR)", + "5808c8aa-7e8f-42e0-85d2-e1e90434cfb3": "Logical Disk Manager (LDM) metadata partition ", + "af9b60a0-1431-4f62-bc68-3311714a69ad": "Logical Disk Manager data partition", + "de94bba4-06d1-4d40-a16a-bfd50179d6ac": "Windows Recovery Environment", + "9f25ee7a-e7b7-11db-94b5-f7e662935912": "Windows Boot Loader", + "_comment_13": "Linux specific GUIDS", + "0fc63daf-8483-4772-8e79-3d69d8477de": "Linux filesystem data", + "44479540-f297-41b2-9af7-d131d5f0458a4": "Root partition (x86)", + "69dad710-2ce4-4e3c-b16c-21a1d49abed3": "Root partition (32-bit ARM)", + "b921b045-1df0-41c3-af44-4c6f280d3fae": "Root partition (64-bit ARM/AArch64)", + "0657fd6d-a4ab-43c4-84e5-0933c84b4f4f": "Swap partition", + "3b8f8425-20e0-4f3b-907f-1a25a76f98e8": "/srv (server data) partition", + "7ffec5c9-2d00-49b7-8941-3ea10a5586b7": "Plain dm-crypt partitiont", + "ca7d7ccb-63ed-4c53-861c-1742536059cc": "LUKS partition", + "_comment_14": "Linux Boot GUIDS https://github.com/linuxboot/linuxboot/blob/master/boards/s2600wf/vendor-files.txt", + "9cfd802c-09a1-43d6-8217-aa49c1f90d2c": "Intel Management Engine BIOS Extension (Mebx)", + "b62efbbb-3923-4cb9-a6e8-db818e828a80": "Intel Management Engine BIOS Extension (Mebx) Setup Browser", + "9ce4325e-003e-11e3-b582-b8ac6f199a57": "Non-Volatile Dual In-line Memory Module (NVDIMM) Driver", + "ea9de6d5-7839-46f7-9e63-4de8b00e2e5d": "NVM DIMM Human Interface Infrastructure (HII)", + "56a1b86f-0d4a-485d-87de-ad0eba1c8c2a": "IBM C Video Gop", + "a1f436ea-a127-4ef8-957c-8048606ff670": "SnpDxe", + "a210f973-229d-4f4d-aa37-9895e6c9eaba": "DpcDxe", + "025bbfc7-e6a9-4b8b-82ad-6815a1aeaf4a": "MNP Network Service Driver - MnpDxe", + "b44b2005-42bc-41c9-80af-abd7dc7d6923": "RSTesSATAEFI", + "15e1e31a-9f9d-4c84-82fb-1a707fc0f63b": "RSTeSATAEFI", + "2cc25173-bd9f-4c89-89cc-29256a3fd9c3": "RSTesSATALegacy", + "bd5d4ca5-674f-4584-8cf9-ce4ea1f54dd1": "RSTeSATALegacy", + "_comment_15": "WinNt GUIDs, add if they are still found in use https://sourceforge.net/p/uefinotes/wiki/FV%20Sources/?version=3", + "fc5c7020-1a48-4198-9be2-ead5abc8cf2f": "BdsDxe", + "d0893f05-b06d-4161-b947-9be9b85ac3a1": "SnpNt32Dxe", + "9b3ada4f-ae56-4c24-8Dea-f03b7558ae50": "PcdPeim", + "34c8c28F-b61c-45a2-8f2e-89e46becc63b": "PeiVariable", + "fe5cea76-4f72-49e8-986f-2cd899dffe5d": "FaultTolerantWriteDxe", + "_comment_16": "Linux Boot Image files UEFI Platform Initialization (PI) specifications Driver Execution Environment (DXE) Architectural protocols and platform modules https://github.com/linuxboot/linuxboot/blob/master/boards/winterfell/image-files.txt", + "5ae3f37e-4eae-41ae-8240-35465b5e81eb": "CORE_DXE", + "cbc59c4a-383a-41eb-a8ee-4498aea567e4": "DXE Runtime", + "3c1de39f-d207-408a-aacc-731cfb7f1dd7": "DXE PciBus", + "80e66e0a-ccd1-43fa-a7b1-2d5ee0f13910": "DXE PciRootBridge", + "9f3a0016-ae55-4288-829d-d22fd344c347": "DXE AmiBoardInfo", + "13ac6dd0-73d0-11d4-b06b-00aa00bd6de7": "DXE EBC", + "e03abadf-e536-4e88-b3a0-b77f78eb34fe": "CPU DXE", + "b7d19491-e55a-470d-8508-85a5dfa41974": "SBDXE", + "e23f86e1-056e-4888-b685-cfcd67c179d4": "DXE SBRun", + "e4ecd0b2-e277-4f2b-becb-e4d75c9a812e": "NBDXE", + "5ad34ba6-f024-2149-52e4-da0398e2bb9": "DXE Services Table", + "_comment_17": "ACPI configuration and tables", + "750890a6-7acf-4f4f-81bd-b400c2bea95a": "AcpiModeEnable", + "d4c05cd1-5eae-431d-a095-13a9e5822045": "MPST", + "db93cb2c-bf1c-431a-abc8-8737bc2afc1f": "PRAD-ACPI-table", + "3bc5b795-a4e0-4d56-9321-316d18a7aefe": "PRAD", + "16d0a23e-c09c-407d-a14a-ad058fdd0ca1": "ACPI", + "26a2481e-4424-46a2-9943-cc4039ead8f8": "S3Save", + "efd652cc-0e99-40f0-96c0-e08c089070fc": "S3Restore", + "8c783970-f02a-4a4d-af09-8797a51eec8d": "PowerManagement", + "299141bb-211a-48a5-92c0-6f9a0a3a006e0": "PowerManagement-ACPI-table", + "2df10014-cf21-4280-8c3f-e539b8ee5150": "PpmPolicyInitDxe", + "4b680e2d-0d63-4f62-b930-7ae995b9b3a3": "SmBusDxe", + "_comment_18": "SMM handlers", + "4a37320b-3fb3-4365-9730-9e89c600395d": "SmmDispatcher", + "753630c9-fae5-47a9-bbbf-88d621cd7282": "SmmChildDispatcher", + "be216ba8-38c4-4535-a6ca-5dca5b43addf": "SmiVariable", + "a56897a1-a77f-4600-84db-22b0a801fa9a": "SmmRuntime", + "d2596f82-f0e1-49fa-95bc-62012c795728": "SmmBase Data", + "69009842-63f2-43db-964b-efad1c39ec85": "SmmBase Data", + "d0632c90-afd7-4492-b186-257c63143c61": "SmmBase", + "7e2d983f-f703-4a29-9761-77b51f5354ed": "SmmCommunicate", + "_comment_19": "CMOS and NVRAM handlers", + "6869c5b3-ac8d-4973-8b37-e354dbf34add": "CmosManagerSmm", + "842a454a-75e5-408b-8b1c-36420e4e3f21": "NvramSmi", + "5446c293-339b-47cd-b719-585de39408cc": "PostReport", + "71ca9ca1-325d-4bfe-afa3-2ec5c94a8680": "DmAcpi", + "cef68c66-06ab-4fb3-a3ed-5ffa885b5725": "SMBiosBoard", + "b13edd38-684c-41ed-a305-d7b7e32497df": "SMBios64", + "ded7956d-7e20-4f20-91a1-190439b04d5b": "SmbiosGetFlashData64", + "daf4bf89-ce71-4917-b522-c89d32fbc59f": "SmbiosStaticData", + "_comment_20": "Apple GUIDS", + "48465300-0000-11aa-aa11-00306543ecac": "Apple Hierarchical File System Plus (HFS+) partition ", + "7c3457ef-0000-11aa-aa11-00306543ecac": "Apple APFS container", + "55465300-0000-11aa-aa11-00306543ecac": "Apple UFS container", + "52414944-0000-11aa-aa11-00306543ecac": "Apple RAID partition", + "4c616265-6c00-11aa-aa11-00306543ecac": "Apple Label", + "53746f72-6167-11aa-aa11-00306543ecac": "Apple Core Storage Container", + "6a898cc3-1dd2-11b2-99a6-080020736631": "ZFS Partition", + "_comment_21": "Chrome OS GUIDS", + "2568845d-2332-4675-bc39-8fa5a4748d15": "Chrome OS kernel ", + "3cb8e202-3b7e-47dd-8a3c-7ff2a13cfcec": "Chrome OS rootfs ", + "2e0a753d-9e48-43b0-8337-b15192cb1b5e": "Chrome OS future use ", + "_comment_22": "Android GUIDS", + "fe3a2a5d-4f32-41a7-b725-accc3285a309": "Android Bootloader", + "114eaffe-1552-4022-b26e-9b053604cf84": "Android Bootloader 2", + "49a4d17f-93a3-45c1-a0de-f50b2ebe2599": "Android Boot", + "4177c722-9e92-4aab-8644-43502bfd5506": "Android Recovery", + "38f428e6-d326-425d-9140-6e0ea133647c": "Android System", + "bd59408b-4514-490d-bf12-9878d963f378": "Android Config", + "8f68cc74-c5e5-48da-be91-a0c8c15e9c80": "Android Factory", + "ac6d7924-eb71-4df8-b48d-e267b27148ff": "Android OEM", + "_comment_23": "MISC GUIDs", + "5023b95c-db26-429b-a648-bd47664c8012": "Built-in EFI Shell", + "610a0202-d308-00c4-0000-000004300d06": "Mystery UUID", + "00000000-0000-0000-0000-000000000000": "Empty UUID" + } +} diff --git a/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/HIRSApplication.java b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/HIRSApplication.java new file mode 100644 index 00000000..d00522e0 --- /dev/null +++ b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/HIRSApplication.java @@ -0,0 +1,36 @@ +package hirs.attestationca.portal; + +import lombok.extern.log4j.Log4j2; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; +import org.springframework.context.annotation.ComponentScan; + +import java.util.Collections; + +@SpringBootApplication +@EnableAutoConfiguration +@Log4j2 +@ComponentScan({"hirs.attestationca.portal", "hirs.attestationca.portal.page.controllers", "hirs.attestationca.persist.entity", "hirs.attestationca.persist.service"}) +public class HIRSApplication extends SpringBootServletInitializer { + + @Override + protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { + return application.sources(HIRSApplication.class); + } + + public static void main(String[] args) { + SpringApplication springApplication = new SpringApplication(HIRSApplication.class); + springApplication.setDefaultProperties(Collections.singletonMap("server.servlet.context-path", "/portal")); + springApplication.run(args); + + log.debug("Debug log message"); + log.info("Info log message"); + log.error("Error log message"); + log.warn("Warn log message"); + log.fatal("Fatal log message"); + log.trace("Trace log message"); + } +} \ No newline at end of file diff --git a/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/HIRSDbInitializer.java b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/HIRSDbInitializer.java new file mode 100644 index 00000000..2d4a8277 --- /dev/null +++ b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/HIRSDbInitializer.java @@ -0,0 +1,59 @@ +package hirs.attestationca.portal; + +import hirs.attestationca.persist.service.SettingsServiceImpl; +import jakarta.servlet.ServletContextListener; +import jakarta.servlet.annotation.WebListener; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; + +@WebListener +public class HIRSDbInitializer implements ServletContextListener { + + @Autowired + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); + @Autowired + static SettingsServiceImpl settingsService = new SettingsServiceImpl(); +// +// public void contextInitialized(final ServletContextEvent servletContextEvent) { +//// AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); +// context.getEnvironment().addActiveProfile("server"); +// context.register(PersistenceJPAConfig.class); +// context.refresh(); +// +// // obtain reference to hibernate session factory +// EntityManager entityManager = context.getBean(EntityManagerFactory.class) +// .createEntityManager(); +// /** +// * This fails if there is an entry already. +// */ +//// entityManager.getTransaction().begin(); +//// entityManager.persist(context.getBean("default-settings")); +//// entityManager.getTransaction().commit(); +// +// insertDefaultEntries(); +// } +// +// /** +// * Insert the ACA's default entries into the DB. This class is invoked after successful +// * install of the HIRS_AttestationCA RPM. +// * +// */ +// public static synchronized void insertDefaultEntries() { +// LOGGER.error("Ensuring default ACA database entries are present."); +// +// // If the SupplyChainAppraiser exists, do not attempt to re-save the supply chain appraiser +// // or SupplyChainSettings +// +// // Create the SupplyChainAppraiser +// LOGGER.error("Saving supply chain appraiser..."); +// +// +// // Create the SupplyChainSettings +// LOGGER.error("Saving default supply chain policy..."); +//// SupplyChainSettings supplyChainPolicy = new SupplyChainSettings( +//// SupplyChainSettings.DEFAULT_POLICY); +// settingsService.saveSettings(new SupplyChainSettings("Default", "Settings are configured for no validation flags set.")); +// +// LOGGER.error("ACA database initialization complete."); +// } +} diff --git a/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/PersistenceJPAConfig.java b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/PersistenceJPAConfig.java new file mode 100644 index 00000000..fa8cad80 --- /dev/null +++ b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/PersistenceJPAConfig.java @@ -0,0 +1,86 @@ +package hirs.attestationca.portal; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.PropertySource; +import org.springframework.core.env.Environment; +import org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; +import org.springframework.jdbc.datasource.DriverManagerDataSource; +import org.springframework.orm.jpa.JpaTransactionManager; +import org.springframework.orm.jpa.JpaVendorAdapter; +import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; +import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter; +import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.annotation.EnableTransactionManagement; + +import javax.sql.DataSource; +import java.util.Properties; + +@Configuration +@EnableTransactionManagement +@PropertySource({ "classpath:hibernate.properties" }) +@ComponentScan({ "hirs.attestationca.portal.page" }) +@EnableJpaRepositories(basePackages = "hirs.attestationca.persist") +public class PersistenceJPAConfig { + + @Autowired + private Environment environment; + + @Bean + public LocalContainerEntityManagerFactoryBean entityManagerFactory() { + final LocalContainerEntityManagerFactoryBean entityManagerBean = new LocalContainerEntityManagerFactoryBean(); + entityManagerBean.setDataSource(dataSource()); + entityManagerBean.setPackagesToScan(new String[] {"hirs.attestationca.persist"}); + + JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter(); + entityManagerBean.setJpaVendorAdapter(vendorAdapter); + entityManagerBean.setJpaProperties(additionalProperties()); + + return entityManagerBean; + } + + @Bean + public DataSource dataSource() { + final DriverManagerDataSource dataSource = new DriverManagerDataSource(); + dataSource.setDriverClassName(environment.getProperty("hibernate.connection.driver_class")); + dataSource.setUrl(environment.getProperty("hibernate.connection.url")); + dataSource.setUsername(environment.getProperty("hibernate.connection.username")); + dataSource.setPassword(environment.getProperty("hibernate.connection.password")); + + return dataSource; + } + + @Bean + public PlatformTransactionManager transactionManager() { + final JpaTransactionManager transactionManager = new JpaTransactionManager(); + transactionManager.setEntityManagerFactory(entityManagerFactory().getObject()); + return transactionManager; + } + + @Bean + public PersistenceExceptionTranslationPostProcessor exceptionTranslation() { + return new PersistenceExceptionTranslationPostProcessor(); + } + + final Properties additionalProperties() { + final Properties hibernateProperties = new Properties(); + hibernateProperties.setProperty("hibernate.hbm2ddl.auto", + environment.getProperty("hibernate.hbm2ddl.auto")); + hibernateProperties.setProperty("hibernate.dialect", + environment.getProperty("hibernate.dialect")); + hibernateProperties.setProperty("hibernate.cache.use_second_level_cache", + "false"); + + return hibernateProperties; + } +// +// @Bean(name="default-settings") +// public SupplyChainSettings supplyChainSettings() { +// SupplyChainSettings scSettings = new SupplyChainSettings("Default", "Settings are configured for no validation flags set."); +// +// return scSettings; +// } +} diff --git a/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/datatables/DataTableResponse.java b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/datatables/DataTableResponse.java new file mode 100644 index 00000000..a1a486e9 --- /dev/null +++ b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/datatables/DataTableResponse.java @@ -0,0 +1,74 @@ +package hirs.attestationca.portal.datatables; + +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; + +/** + * A Wrapper for Data Table JSON responses. Allows Spring to serialize a data object with additional + * meta data required by data tables. + * + * @param the type of object that is being wrapped. + */ +@NoArgsConstructor(access = AccessLevel.PUBLIC) +public final class DataTableResponse { + + private List data = new LinkedList(); + @Getter @Setter + private int draw; + @Getter @Setter + private long recordsTotal, recordsFiltered; + + /** + * Builds a data table response using a FilteredRecordList. + * + * @param recordList the filtered record list + * @param inputQuery the data table input (used for draw) + */ +// public DataTableResponse(final FilteredRecordsList recordList, +// final DataTableInput inputQuery) { +// this(recordList, inputQuery.getDraw(), +// recordList.getRecordsTotal(), recordList.getRecordsFiltered()); +// } + + /** + * Constructs a data table response using the specified data with the data table specific + * information. + * + * @param data that is to be displayed by data table + * @param draw the originating draw request ID (usually from a web request) + * @param recordsTotal total number of records inside the data + * @param recordsFiltered number of records excluded from the request + */ + public DataTableResponse(final List data, final int draw, final long recordsTotal, + final long recordsFiltered) { + setData(data); + this.draw = draw; + this.recordsTotal = recordsTotal; + this.recordsFiltered = recordsFiltered; + } + + /** + * Gets the data table data. + * + * @return the data + */ + public List getData() { + return Collections.unmodifiableList(data); + } + + /** + * Sets the data table data. + * + * @param data the data + */ + public void setData(final List data) { + this.data.clear(); + this.data.addAll(data); + } +} diff --git a/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/package-info.java b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/package-info.java new file mode 100644 index 00000000..1fa641aa --- /dev/null +++ b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/package-info.java @@ -0,0 +1,4 @@ +/** + * Root Package for HIRS Attestation CA Portal. + */ +package hirs.attestationca.portal; diff --git a/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/Page.java b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/Page.java new file mode 100644 index 00000000..219e4982 --- /dev/null +++ b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/Page.java @@ -0,0 +1,220 @@ +package hirs.attestationca.portal.page; + +import hirs.utils.VersionHelper; + +/** + * Contains attributes required to display a portal page and its menu link. + */ +public enum Page { + + /** + * Site landing page. + */ + INDEX("HIRS Attestation CA", "Version: " + VersionHelper.getVersion(), + null, false, false, null, null), + /** + * Page to import and manage trust chains. + */ + TRUST_CHAIN("Trust Chain Management", "ic_store", + null, "certificate-request/"), + /** + * Page to display and manage endorsement key credentials. + */ + ENDORSEMENT_KEY_CREDENTIALS("Endorsement Key Certificates", "ic_vpn_key", + "first", "certificate-request/"), + /** + * Page to display and manage platform credentials. + */ + PLATFORM_CREDENTIALS("Platform Certificates", "ic_important_devices", + null, "certificate-request/"), + /** + * Page to display issued certificates. + */ + ISSUED_CERTIFICATES("Issued Certificates", "ic_library_books", + null, "certificate-request/"), + /** + * Non-menu page to display certificate. Reachable from all certificate pages. + */ + CERTIFICATE_DETAILS("Certificate Details", "", null, true, false, null, null), + /** + * Page to display registered devices. + */ + DEVICES("Devices", "ic_devices", "first"), + /** + * Page to display RIMs. + */ + REFERENCE_MANIFESTS("Reference Integrity Manifests", + "ic_important_devices", "first"), + /** + * Non-menu page to display rims. + */ + RIM_DETAILS("Reference Integrity Manifest Details", + "", null, true, false, null, null), + /** + * Page to display RIM event digest table. + */ + RIM_DATABASE("RIM Database", "ic_important_devices", "first"), + /** + * Page that manages Attestation CA Policy. + */ + POLICY("Policy", "ic_subtitles"), + /** + * Help page. + */ + HELP("Help", "ic_live_help"); + + private final String title; + private final String subtitle; + private final String icon; + + private final boolean hasMenu; + private final String menuLinkClass; + private final boolean inMenu; + + private final String prefixPath; + private final String viewName; + + /** + * Constructor for Page. + * + * @param title title of the page + * @param subtitle subtitle of the page + * @param icon icon for the page + * @param hasMenu the page has its own menu + * @param inMenu the page appears in a menu + * @param menuLinkClass the category to which this page belongs + * @param prefixPath prefix path that appears in the URL for this page + */ + Page(final String title, + final String subtitle, + final String icon, + final boolean hasMenu, + final boolean inMenu, + final String menuLinkClass, + final String prefixPath) { + this.title = title; + this.subtitle = subtitle; + this.icon = icon; + this.hasMenu = hasMenu; + this.menuLinkClass = menuLinkClass; + this.inMenu = inMenu; + this.prefixPath = prefixPath; + + viewName = this.name().toLowerCase().replaceAll("_", "-"); + } + + /** + * Constructor for Page. + * + * @param title title of the page + * @param icon icon for the page + * @param menuLinkClass the category to which this page belongs + * @param prefixPath prefix path that appears in the URL for this page + */ + Page(final String title, + final String icon, + final String menuLinkClass, + final String prefixPath) { + this(title, null, icon, true, true, menuLinkClass, prefixPath); + } + + /** + * Constructor for Page. + * + * @param title title of the page + * @param icon icon for the page + * @param menuLinkClass the category to which this page belongs + */ + Page(final String title, + final String icon, + final String menuLinkClass) { + this(title, null, icon, true, true, menuLinkClass, null); + } + + /** + * Constructor for Page. + * + * @param title title of the page + * @param icon icon for the page + */ + Page(final String title, + final String icon) { + this(title, null, icon, true, true, null, null); + } + + /** + * Returns the title of the page. + * + * @return the title of the page. + */ + public String getTitle() { + return title; + } + + /** + * Returns the subtitle of the page. + * + * @return the subtitle of the page. + */ + public String getSubtitle() { + return subtitle; + } + + /** + * Returns the base filename of the icon for page. E.g. "ic_my_icon", which will be appended + * with appropriate size string (_24dp/_48dp) and file extension (.png) when used. + * + * @return the base filename of the icon for page. + */ + public String getIcon() { + return icon; + } + + /** + * Returns true if the page should be displayed in the navigation menu. + * + * @return true if the page should be displayed in the navigation menu. + */ + public boolean getInMenu() { + return inMenu; + } + + /** + * Returns the css class to add to the menu link to display it appropriately. E.g. "first" if + * the link is the first in a group to separate it visually from the previous group. + * + * @return he class to add to the menu link to display it appropriately. + */ + public String getMenuLinkClass() { + return menuLinkClass; + } + + /** + * Returns true if the page should display the navigation menu. + * + * @return true if the page should display the navigation menu. + */ + public boolean getHasMenu() { + return hasMenu; + } + + /** + * Return the page's view name. + * + * @return the page's view name + */ + public String getViewName() { + return viewName; + } + + /** + * Return the page's view name. + * + * @return the page's view name + */ + public String getPrefixPath() { + return prefixPath; + } + +} + diff --git a/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/PageController.java b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/PageController.java new file mode 100644 index 00000000..4ab1306e --- /dev/null +++ b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/PageController.java @@ -0,0 +1,169 @@ +package hirs.attestationca.portal.page; + +import hirs.utils.BannerConfiguration; +import lombok.AllArgsConstructor; +import lombok.extern.log4j.Log4j2; +import org.apache.http.client.utils.URIBuilder; +import org.springframework.ui.ExtendedModelMap; +import org.springframework.ui.Model; +import org.springframework.ui.ModelMap; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.servlet.ModelAndView; +import org.springframework.web.servlet.mvc.support.RedirectAttributes; +import org.springframework.web.servlet.view.RedirectView; + +import java.io.IOException; +import java.net.URISyntaxException; +import java.util.Map; +import java.util.Optional; + +/** + * Abstract class to provide common functionality for page Controllers. + * + * @param

PageParams class used by the subclass. + */ +@Log4j2 +@AllArgsConstructor +public abstract class PageController

{ + + /** + * Model attribute name used by initPage for the initial data passed to the page. + */ + public static final String INITIAL_DATA = "initialData"; + + /** + * Reserved attribute used by page.tag to identify a page's general + * information. + */ + public static final String PAGE_ATTRIBUTE = "page"; + + /** + * Reserved attribute used by page.tag to identify the page collection used + * for navigation. + */ + public static final String PAGES_ATTRIBUTE = "pages"; + + /** + * Reserved attribute used by page.tag to identify the banner information. + */ + public static final String BANNER_ATTRIBUTE = "banner"; + + /** + * Reserved attribute used by page.tag to identify the messages the page + * should display. + */ + public static final String MESSAGES_ATTRIBUTE = "messages"; + + private final Page page; + + /** + * Returns the path for the view and the data model for the page. + * + * @param params The object to map url parameters into. + * @param model The data model for the request. Can contain data from + * redirect. + * @return the path for the view and data model for the page. + */ + @RequestMapping + public abstract ModelAndView initPage(@ModelAttribute P params, Model model); + + /** + * Creates a generic ModelAndView containing this page's configuration and + * the list of other pages for navigational purposes. + * + * @return A generic ModelAndView containing basic information for the page. + */ + protected final ModelAndView getBaseModelAndView() { + return getBaseModelAndView(page); + } + + /** + * Creates a generic ModelAndView containing the specify page + * configuration and the list of other pages for navigational + * purposes. + * + * @param newPage new page to get the model and view + * @return A generic ModelAndView containing basic information for the page. + */ + protected final ModelAndView getBaseModelAndView(final Page newPage) { + ModelMap modelMap = new ExtendedModelMap(); + + // add page information + modelMap.addAttribute(PAGE_ATTRIBUTE, newPage); + + // add other pages for navigation + modelMap.addAttribute(PAGES_ATTRIBUTE, Page.values()); + + // add banner information + try { + BannerConfiguration banner = new BannerConfiguration(); + modelMap.addAttribute(BANNER_ATTRIBUTE, banner); + } catch (IOException ex) { + modelMap.addAttribute(BANNER_ATTRIBUTE, null); + } + + return new ModelAndView(newPage.getViewName(), modelMap); + } + + /** + * Redirects back to this controller's page with the specified data. + * + * @param params The url parameters to pass to the page. + * @param model The model data to pass to the page. + * @param attr The request's RedirectAttributes to hold the model data. + * @return RedirectView back to the page with the specified parameters. + * @throws URISyntaxException if malformed URI + */ + protected final RedirectView redirectToSelf( + final P params, + final Map model, + final RedirectAttributes attr) throws URISyntaxException { + + return redirectTo(page, params, model, attr); + } + + /** + * Redirects controller's page with the specified data. + * + * @param newPage new page to get the model and view + * @param params The url parameters to pass to the page. + * @param model The model data to pass to the page. + * @param attr The request's RedirectAttributes to hold the model data. + * @return RedirectView back to the page with the specified parameters. + * @throws URISyntaxException if malformed URI + */ + protected final RedirectView redirectTo( + final Page newPage, + final P params, + final Map model, + final RedirectAttributes attr) throws URISyntaxException { + + String defaultUri = "../" + newPage.getViewName(); + // create uri with specified parameters + URIBuilder uri = new URIBuilder("../" + newPage.getViewName()); + log.debug("Redirection URI = " + uri.toString()); + + if (params != null) { + for (Map.Entry e : params.asMap().entrySet()) { + Object v = Optional.ofNullable(e.getValue()).orElse(""); + uri.addParameter(e.getKey(), v.toString()); + } + } + + // create view + RedirectView redirect = new RedirectView(defaultUri); + + // do not put model attributes in the url + redirect.setExposeModelAttributes(false); + + // add model data to forward to redirected page + if (model != null) { + for (Map.Entry e : model.entrySet()) { + attr.addFlashAttribute(e.getKey(), e.getValue()); + } + } + + return redirect; + } +} diff --git a/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/PageMessages.java b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/PageMessages.java new file mode 100644 index 00000000..437070b7 --- /dev/null +++ b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/PageMessages.java @@ -0,0 +1,70 @@ +package hirs.attestationca.portal.page; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Encapsulates error, success, and informational messages to display on a page. + */ +public class PageMessages { + + private final List error = new ArrayList<>(); + private final List success = new ArrayList<>(); + private final List info = new ArrayList<>(); + + /** + * Returns the list of error messages. + * + * @return the list of error messages. + */ + public List getError() { + return Collections.unmodifiableList(error); + } + + /** + * Adds an error message. + * + * @param error the error message to add + */ + public void addError(final String error) { + this.error.add(error); + } + + /** + * Returns the list of success messages. + * + * @return the list of success messages. + */ + public List getSuccess() { + return Collections.unmodifiableList(success); + } + + /** + * Adds a success message. + * + * @param success the success message to add + */ + public void addSuccess(final String success) { + this.success.add(success); + } + + /** + * Returns the list of informational messages. + * + * @return the list of informational messages. + */ + public List getInfo() { + return Collections.unmodifiableList(info); + } + + /** + * Adds an informational message. + * + * @param info the informational message to add + */ + public void addInfo(final String info) { + this.info.add(info); + } + +} diff --git a/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/PageParams.java b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/PageParams.java new file mode 100644 index 00000000..c035fb26 --- /dev/null +++ b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/PageParams.java @@ -0,0 +1,17 @@ +package hirs.attestationca.portal.page; + +import java.util.LinkedHashMap; + +/** + * Interface for a page's url parameters. + */ +public interface PageParams { + + /** + * Allows PageController to iterate over the url parameters. + * + * @return map containing the object's url parameters. + */ + LinkedHashMap asMap(); + +} \ No newline at end of file diff --git a/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/PolicyPageModel.java b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/PolicyPageModel.java new file mode 100644 index 00000000..dbf8d37a --- /dev/null +++ b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/PolicyPageModel.java @@ -0,0 +1,80 @@ +package hirs.attestationca.portal.page; + +import hirs.attestationca.persist.entity.userdefined.SupplyChainSettings; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; + +/** + * PolicyPage model object to demonstrate data exchange between policy.jsp page + * form form and controller. + */ +@Setter +@Getter +@ToString +@NoArgsConstructor +public class PolicyPageModel { + // Variables to communicate policy settings to page + private boolean enableEcValidation; + private boolean enablePcCertificateValidation; + private boolean enablePcCertificateAttributeValidation; + private boolean enableFirmwareValidation; + private boolean issueAttestationCertificate; + private boolean issueDevIdCertificate; + private boolean generateOnExpiration; + private boolean devIdExpirationFlag; + private boolean enableIgnoreIma; + private boolean enableIgnoreTboot; + private boolean enableIgnoreGpt; + private boolean enableIgnoreOsEvt; + + // Variables to get policy settings from page + private String pcValidate; + private String pcAttributeValidate; + private String ecValidate; + private String fmValidate; + private String attestationCertificateIssued; + private String devIdCertificateIssued; + private String generationExpirationOn; + private String devIdExpirationChecked; + private String numOfValidDays; + private String reissueThreshold; + private String devIdReissueThreshold; + private String ignoreIma; + private String ignoretBoot; + private String ignoreGpt; + private String ignoreOsEvt; + private String expirationValue; + private String devIdExpirationValue; + private String thresholdValue; + private String devIdThresholdValue; + + /** + * Constructor. Sets fields from policy. + * + * @param policy The supply chain policy + */ + public PolicyPageModel(final SupplyChainSettings policy) { + this.enableEcValidation = policy.isEcValidationEnabled(); + this.enablePcCertificateValidation = policy.isPcValidationEnabled(); + this.enablePcCertificateAttributeValidation = policy.isPcAttributeValidationEnabled(); + this.enableFirmwareValidation = policy.isFirmwareValidationEnabled(); + this.issueAttestationCertificate = policy.isIssueAttestationCertificate(); + this.issueDevIdCertificate = policy.isIssueDevIdCertificate(); + this.generateOnExpiration = policy.isGenerateOnExpiration(); + this.devIdExpirationFlag = policy.isDevIdExpirationFlag(); + this.numOfValidDays = policy.getValidityDays(); + this.reissueThreshold = policy.getReissueThreshold(); + this.expirationValue = policy.getValidityDays(); + this.thresholdValue = policy.getReissueThreshold(); + this.devIdExpirationValue = policy.getDevIdValidityDays(); + this.devIdReissueThreshold = policy.getDevIdReissueThreshold(); + this.devIdThresholdValue = policy.getDevIdReissueThreshold(); + // pcrPolicy + this.enableIgnoreIma = policy.isIgnoreImaEnabled(); + this.enableIgnoreTboot = policy.isIgnoretBootEnabled(); + this.enableIgnoreGpt = policy.isIgnoreGptEnabled(); + this.enableIgnoreOsEvt = policy.isIgnoreOsEvtEnabled(); + } +} diff --git a/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/CertificateDetailsPageController.java b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/CertificateDetailsPageController.java new file mode 100644 index 00000000..75bef9eb --- /dev/null +++ b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/CertificateDetailsPageController.java @@ -0,0 +1,119 @@ +package hirs.attestationca.portal.page.controllers; + +import hirs.attestationca.portal.page.Page; +import hirs.attestationca.persist.service.CertificateServiceImpl; +import hirs.attestationca.portal.page.PageController; +import hirs.attestationca.portal.page.PageMessages; +import hirs.attestationca.portal.page.params.CertificateDetailsPageParams; +import hirs.attestationca.portal.page.utils.CertificateStringMapBuilder; +import lombok.extern.log4j.Log4j2; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.servlet.ModelAndView; + +import java.io.IOException; +import java.util.HashMap; +import java.util.UUID; + +/** + * Controller for the Certificate Details page. + */ +@Log4j2 +@Controller +@RequestMapping("/certificate-details") +public class CertificateDetailsPageController extends PageController { + + /** + * Model attribute name used by initPage for the initial data passed to the page. + */ + static final String INITIAL_DATA = "initialData"; + private final CertificateServiceImpl certificateServiceImpl; + + /** + * Constructor providing the Page's display and routing specification. + * @param certificateServiceImpl the certificate manager + */ + @Autowired + public CertificateDetailsPageController(final CertificateServiceImpl certificateServiceImpl) { + super(Page.CERTIFICATE_DETAILS); + this.certificateServiceImpl = certificateServiceImpl; + } + + /** + * Returns the path for the view and the data model for the page. + * + * @param params The object to map url parameters into. + * @param model The data model for the request. Can contain data from redirect. + * @return the path for the view and data model for the page. + */ + @Override + @RequestMapping + public ModelAndView initPage(final CertificateDetailsPageParams params, final Model model) { + // get the basic information to render the page + ModelAndView mav = getBaseModelAndView(); + PageMessages messages = new PageMessages(); + + // Map with the certificate information + HashMap data = new HashMap<>(); + + // Check if parameters were set + if (params.getId() == null) { + String typeError = "ID was not provided"; + messages.addError(typeError); + log.error(typeError); + mav.addObject(MESSAGES_ATTRIBUTE, messages); + } else if (params.getType() == null) { + String typeError = "Type was not provided"; + messages.addError(typeError); + log.error(typeError); + mav.addObject(MESSAGES_ATTRIBUTE, messages); + } else { + try { + String type = params.getType().toLowerCase(); + UUID uuid = UUID.fromString(params.getId()); + switch (type) { + case "certificateauthority": + data.putAll(CertificateStringMapBuilder.getCertificateAuthorityInformation( + uuid, certificateServiceImpl)); + break; + case "endorsement": + data.putAll(CertificateStringMapBuilder.getEndorsementInformation(uuid, + certificateServiceImpl)); + break; + case "platform": + data.putAll(CertificateStringMapBuilder.getPlatformInformation(uuid, + certificateServiceImpl)); + break; + case "issued": + data.putAll(CertificateStringMapBuilder.getIssuedInformation(uuid, + certificateServiceImpl)); + break; + default: + String typeError = "Invalid certificate type: " + params.getType(); + messages.addError(typeError); + log.error(typeError); + mav.addObject(MESSAGES_ATTRIBUTE, messages); + break; + } + } catch (IllegalArgumentException | IOException ex) { + String uuidError = "Failed to parse ID from: " + params.getId(); + messages.addError(uuidError); + log.error(uuidError, ex); + } + + if (data.isEmpty()) { + String notFoundMessage = "Unable to find certificate with ID: " + params.getId(); + messages.addError(notFoundMessage); + log.warn(notFoundMessage); + mav.addObject(MESSAGES_ATTRIBUTE, messages); + } else { + mav.addObject(INITIAL_DATA, data); + } + } + + // return the model and view + return mav; + } +} 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 new file mode 100644 index 00000000..78190fb1 --- /dev/null +++ b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/CertificatePageController.java @@ -0,0 +1,73 @@ +package hirs.attestationca.portal.page.controllers; + +import hirs.attestationca.persist.entity.userdefined.certificate.CertificateAuthorityCredential; +import hirs.attestationca.persist.service.CertificateServiceImpl; +import hirs.attestationca.portal.page.Page; +import hirs.attestationca.portal.page.PageController; +import hirs.attestationca.portal.page.params.NoPageParams; +import lombok.extern.log4j.Log4j2; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.servlet.ModelAndView; + +@Log4j2 +@Controller +@RequestMapping("/certificate-request") +public class CertificatePageController extends PageController { + + private final CertificateServiceImpl certificateServiceImpl; + private CertificateAuthorityCredential certificateAuthorityCredential; + + private static final String TRUSTCHAIN = "trust-chain"; + private static final String PLATFORMCREDENTIAL = "platform-credentials"; + private static final String ENDORSEMENTCREDENTIAL = "endorsement-key-credentials"; + private static final String ISSUEDCERTIFICATES = "issued-certificates"; + + /** + * Model attribute name used by initPage for the aca cert info. + */ + static final String ACA_CERT_DATA = "acaCertData"; + + /** + * Constructor providing the Page's display and routing specification. + * + * @param certificateServiceImpl the certificate manager + * @param crudManager the CRUD manager for certificates + * @param acaCertificate the ACA's X509 certificate + */ + @Autowired + public CertificatePageController( + final CertificateServiceImpl certificateServiceImpl +// final CrudManager crudManager, +// final X509Certificate acaCertificate + ) { + super(Page.TRUST_CHAIN); + this.certificateServiceImpl = certificateServiceImpl; +// this.dataTableQuerier = crudManager; + +// try { +//// certificateAuthorityCredential +//// = new CertificateAuthorityCredential(acaCertificate.getEncoded()); +// } catch (IOException e) { +// log.error("Failed to read ACA certificate", e); +// } catch (CertificateEncodingException e) { +// log.error("Error getting encoded ACA certificate", e); +// } + } + + /** + * Returns the path for the view and the data model for the page. + * + * @param params The object to map url parameters into. + * @param model The data model for the request. Can contain data from + * redirect. + * @return the path for the view and data model for the page. + */ + @Override + @RequestMapping + public ModelAndView initPage(final NoPageParams params, final Model model) { + return getBaseModelAndView(); + } +} 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 new file mode 100644 index 00000000..c4dec9c0 --- /dev/null +++ b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/DevicePageController.java @@ -0,0 +1,60 @@ +package hirs.attestationca.portal.page.controllers; + +import hirs.attestationca.persist.entity.manager.DeviceRepository; +import hirs.attestationca.persist.entity.userdefined.Device; +import hirs.attestationca.portal.page.Page; +import hirs.attestationca.persist.service.DeviceServiceImpl; +import hirs.attestationca.portal.page.PageController; +import hirs.attestationca.portal.page.params.NoPageParams; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.servlet.ModelAndView; + +@Controller +@RequestMapping("/devices") +public class DevicePageController extends PageController { + /** + * https://odrotbohm.de/2013/11/why-field-injection-is-evil/ + * + * Autowiring property vs constructor + */ + + private final DeviceServiceImpl deviceServiceImpl; + private final DeviceRepository deviceRepository; + + @Autowired + public DevicePageController(DeviceServiceImpl deviceServiceImpl, + DeviceRepository deviceRepository) { + super(Page.DEVICES); + this.deviceServiceImpl = deviceServiceImpl; + this.deviceRepository = deviceRepository; + } + + @Override + @RequestMapping + public ModelAndView initPage(final NoPageParams params, final Model model) { + return getBaseModelAndView(); + } + +// @RequestMapping(value = "list", produces = MediaType.APPLICATION_JSON_VALUE, +// method = RequestMethod.GET) +// public DataTableResponse> getTableData( +// final DataTableInput input) { +// String orderColumnName = input.getOrderColumnName(); +// FilteredRecordsList> record +// = retrieveDevicesAndAssociatedCertificates(deviceList); +// modelMap.put("devices", deviceServiceImpl.retrieveDevices()); +// return new DataTableResponse<>(record, input); +// } + + + @GetMapping(path="/all") + public @ResponseBody Iterable getAllDevices() { + return deviceRepository.findAll(); + } + +} \ No newline at end of file diff --git a/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/ErrorController.java b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/ErrorController.java new file mode 100644 index 00000000..90391ffd --- /dev/null +++ b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/ErrorController.java @@ -0,0 +1,23 @@ +package hirs.attestationca.portal.page.controllers; + +import jakarta.servlet.http.HttpServletRequest; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.servlet.ModelAndView; + + +@Controller("error") +public class ErrorController { + + @ExceptionHandler(Exception.class) + public ModelAndView handleException(HttpServletRequest request, Exception ex) { + ModelAndView modelAndView = new ModelAndView(); + + modelAndView.addObject("exception", ex.getLocalizedMessage()); + modelAndView.addObject("url", request.getRequestURL()); + + modelAndView.setViewName("error"); + + return modelAndView; + } +} diff --git a/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/IndexPageController.java b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/IndexPageController.java new file mode 100644 index 00000000..2c16fcf2 --- /dev/null +++ b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/IndexPageController.java @@ -0,0 +1,42 @@ +package hirs.attestationca.portal.page.controllers; + +import hirs.attestationca.portal.page.Page; +import hirs.attestationca.portal.page.PageController; +import hirs.attestationca.portal.page.params.NoPageParams; +import lombok.extern.log4j.Log4j2; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.servlet.ModelAndView; + +@Controller +@Log4j2 +@RequestMapping("/index") +public class IndexPageController extends PageController { + + /** + * Constructor providing the Page's display and routing specification. + */ + public IndexPageController() { + super(Page.INDEX); + } + + /** + * Returns the path for the view and the data model for the page. + * + * @param params The object to map url parameters into. + * @param model The data model for the request. Can contain data from redirect. + * @return the path for the view and data model for the page. + */ + @Override + @RequestMapping + public ModelAndView initPage(final NoPageParams params, final Model model) { + return getBaseModelAndView(); + } + +// @RequestMapping(value = "/", method = RequestMethod.GET) +// public String showIndexPage(ModelMap model) { +// model.put("name", "welcome"); +// return "welcome"; +// } +} diff --git a/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/LombokLoggingController.java b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/LombokLoggingController.java new file mode 100644 index 00000000..197ecc42 --- /dev/null +++ b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/LombokLoggingController.java @@ -0,0 +1,21 @@ +package hirs.attestationca.portal.page.controllers; + +import lombok.extern.log4j.Log4j2; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@Log4j2 +public class LombokLoggingController { + + @RequestMapping("/lombok") + public String index() { + log.trace("A TRACE Message"); + log.debug("A DEBUG Message"); + log.info("An INFO Message"); + log.warn("A WARN Message"); + log.error("An ERROR Message"); + + return "Howdy! Check out the Logs to see the output..."; + } +} diff --git a/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/PolicyPageController.java b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/PolicyPageController.java new file mode 100644 index 00000000..6e703abd --- /dev/null +++ b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/PolicyPageController.java @@ -0,0 +1,962 @@ +package hirs.attestationca.portal.page.controllers; + +import hirs.attestationca.persist.entity.userdefined.SupplyChainSettings; +import hirs.attestationca.persist.service.SettingsServiceImpl; +import hirs.attestationca.portal.page.Page; +import hirs.attestationca.portal.page.PageController; +import hirs.attestationca.portal.page.PageMessages; +import hirs.attestationca.portal.page.PolicyPageModel; +import hirs.attestationca.portal.page.params.NoPageParams; +import hirs.utils.exception.PolicyManagerException; +import lombok.extern.log4j.Log4j2; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.servlet.ModelAndView; +import org.springframework.web.servlet.mvc.support.RedirectAttributes; +import org.springframework.web.servlet.view.RedirectView; + +import java.net.URISyntaxException; +import java.util.HashMap; +import java.util.Map; + +/** + * Controller for the Policy page. + */ +@Log4j2 +@Controller +@RequestMapping("/policy") +public class PolicyPageController extends PageController { + + /** + * Represents a web request indicating to enable a setting (based on radio + * buttons from a web form). + */ + private static final String ENABLED_CHECKED_PARAMETER_VALUE = "checked"; + + private static final String ENABLED_EXPIRES_PARAMETER_VALUE = "expires"; + + private SettingsServiceImpl settingsService; + + /** + * Model attribute name used by initPage for the initial data passed to the + * page. + */ + public static final String INITIAL_DATA = "initialData"; + + /** + * Flash attribute name used by initPage and post for the data forwarded + * during the redirect from the POST operation back to the page. + */ + public static final String RESULT_DATA = "resultData"; + + /** + * Constructor. + * + * @param policyService the policy service + */ + @Autowired + public PolicyPageController(final SettingsServiceImpl policyService) { + super(Page.POLICY); + this.settingsService = policyService; + + if (this.settingsService.getByName("Default") == null) { + this.settingsService.saveSettings(new SupplyChainSettings("Default", "Settings are configured for no validation flags set.")); + } + } + + /** + * Returns the path for the view and the data model for the page. + * + * @param params The object to map url parameters into. + * @param model The data model for the request. Can contain data from + * redirect. + * @return the path for the view and data model for the page. + */ + @Override + @RequestMapping + public ModelAndView initPage(final NoPageParams params, final Model model) { + // get the basic information to render the page + ModelAndView mav = getBaseModelAndView(); + + SupplyChainSettings policy = getDefaultPolicy(); + log.debug(policy); + PolicyPageModel pageModel = new PolicyPageModel(policy); + mav.addObject(INITIAL_DATA, pageModel); + + log.debug(pageModel); + + return mav; + } + + /** + * Updates the Platform Cert Validation policy setting and redirects back to + * the original page. + * + * @param ppModel The data posted by the form mapped into an object. + * @param attr RedirectAttributes used to forward data back to the original + * page. + * @return View containing the url and parameters + * @throws URISyntaxException if malformed URI + */ + @RequestMapping(value = "update-pc-validation", method = RequestMethod.POST) + public RedirectView updatePcVal(@ModelAttribute final PolicyPageModel ppModel, + final RedirectAttributes attr) throws URISyntaxException { + + Map model = new HashMap<>(); + PageMessages messages = new PageMessages(); + String successMessage; + boolean pcValidationOptionEnabled + = ppModel.getPcValidate().equalsIgnoreCase(ENABLED_CHECKED_PARAMETER_VALUE); + + try { + SupplyChainSettings policy = getDefaultPolicyAndSetInModel(ppModel, model); + + // If PC policy setting change results in invalid policy, inform user + if (!isPolicyValid(policy.isEcValidationEnabled(), pcValidationOptionEnabled, + policy.isPcAttributeValidationEnabled())) { + handleUserError(model, messages, + "Unable to change Platform Validation setting," + + " invalid policy configuration."); + return redirectToSelf(new NoPageParams(), model, attr); + } + // set the policy option and create display message + if (pcValidationOptionEnabled) { + policy.setPcValidationEnabled(true); + successMessage = "Platform certificate validation enabled"; + } else { + policy.setPcValidationEnabled(false); + policy.setPcAttributeValidationEnabled(false); + successMessage = "Platform certificate validation disabled"; + } + savePolicyAndApplySuccessMessage(ppModel, model, messages, successMessage, policy); + + } catch (PolicyManagerException pmEx) { + // Log and return any error messages to the user + handlePolicyManagerUpdateError(model, messages, pmEx, + "Error changing ACA platform validation Policy", + "Error updating policy. \n" + pmEx.getMessage()); + } + return redirectToSelf(new NoPageParams(), model, attr); + } + + /** + * Updates the Platform Cert Attribute Validation policy setting and + * redirects back to the original page. + * + * @param ppModel The data posted by the form mapped into an object. + * @param attr RedirectAttributes used to forward data back to the original + * page. + * @return View containing the url and parameters + * @throws URISyntaxException if malformed URI + */ + @RequestMapping(value = "update-pc-attribute-validation", method = RequestMethod.POST) + public RedirectView updatePcAttributeVal(@ModelAttribute final PolicyPageModel ppModel, + final RedirectAttributes attr) + throws URISyntaxException { + Map model = new HashMap<>(); + PageMessages messages = new PageMessages(); + String successMessage; + boolean pcAttributeValidationOptionEnabled = ppModel.getPcAttributeValidate() + .equalsIgnoreCase(ENABLED_CHECKED_PARAMETER_VALUE); + + try { + SupplyChainSettings policy = getDefaultPolicyAndSetInModel(ppModel, model); + + // If PC Attribute Validation is enabled without PC Validation, disallow change + if (!isPolicyValid(policy.isEcValidationEnabled(), + policy.isPcValidationEnabled(), pcAttributeValidationOptionEnabled)) { + + handleUserError(model, messages, + "To enable Platform Attribute Validation, Platform Credential Validation" + + " must also be enabled."); + return redirectToSelf(new NoPageParams(), model, attr); + } + // set the policy option and create display message + if (pcAttributeValidationOptionEnabled) { + policy.setPcAttributeValidationEnabled(true); + successMessage = "Platform certificate attribute validation enabled"; + } else { + policy.setPcAttributeValidationEnabled(false); + successMessage = "Platform certificate attribute validation disabled"; + } + savePolicyAndApplySuccessMessage(ppModel, model, messages, successMessage, policy); + } catch (PolicyManagerException pmEx) { + // Log and return any error messages to the user + handlePolicyManagerUpdateError(model, messages, pmEx, + "Error changing ACA platform certificate attribute validation policy", + "Error updating policy. \n" + pmEx.getMessage()); + } + return redirectToSelf(new NoPageParams(), model, attr); + } + + /** + * Updates the Attestation Certificate generation policy setting and redirects + * back to the original page. + * + * @param ppModel The data posted by the form mapped into an object. + * @param attr RedirectAttributes used to forward data back to the original page. + * @return View containing the url and parameters + * @throws URISyntaxException if malformed URI + */ + @RequestMapping(value = "update-issue-attestation", method = RequestMethod.POST) + public RedirectView updateAttestationVal(@ModelAttribute final PolicyPageModel ppModel, + final RedirectAttributes attr) + throws URISyntaxException { + + // set the data received to be populated back into the form + Map model = new HashMap<>(); + PageMessages messages = new PageMessages(); + String successMessage; + boolean issuedAttestationOptionEnabled + = ppModel.getAttestationCertificateIssued() + .equalsIgnoreCase(ENABLED_CHECKED_PARAMETER_VALUE); + + try { + SupplyChainSettings policy = getDefaultPolicyAndSetInModel(ppModel, model); + + if (issuedAttestationOptionEnabled) { + successMessage = "Attestation Certificate generation enabled."; + } else { + successMessage = "Attestation Certificate generation disabled."; + policy.setGenerateOnExpiration(false); + } + + policy.setIssueAttestationCertificate(issuedAttestationOptionEnabled); + savePolicyAndApplySuccessMessage(ppModel, model, messages, successMessage, policy); + } catch (PolicyManagerException pmEx) { + handlePolicyManagerUpdateError(model, messages, pmEx, + "Error changing ACA Attestation Certificate generation policy", + "Error updating policy. \n" + pmEx.getMessage()); + } + + // return the redirect + return redirectToSelf(new NoPageParams(), model, attr); + } + + /** + * Updates the DevID Certificate generation policy setting and redirects + * back to the original page. + * + * @param ppModel The data posted by the form mapped into an object. + * @param attr RedirectAttributes used to forward data back to the original page. + * @return View containing the url and parameters + * @throws URISyntaxException if malformed URI + */ + @RequestMapping(value = "update-issue-devid", method = RequestMethod.POST) + public RedirectView updateDevIdVal(@ModelAttribute final PolicyPageModel ppModel, + final RedirectAttributes attr) + throws URISyntaxException { + + // set the data received to be populated back into the form + Map model = new HashMap<>(); + PageMessages messages = new PageMessages(); + String successMessage; + boolean issuedDevIdOptionEnabled + = ppModel.getDevIdCertificateIssued() + .equalsIgnoreCase(ENABLED_CHECKED_PARAMETER_VALUE); + + try { + SupplyChainSettings policy = getDefaultPolicyAndSetInModel(ppModel, model); + + if (issuedDevIdOptionEnabled) { + successMessage = "DevID Certificate generation enabled."; + } else { + successMessage = "DevID Certificate generation disabled."; + policy.setDevIdExpirationFlag(false); + } + + policy.setIssueDevIdCertificate(issuedDevIdOptionEnabled); + savePolicyAndApplySuccessMessage(ppModel, model, messages, successMessage, policy); + } catch (PolicyManagerException pmEx) { + handlePolicyManagerUpdateError(model, messages, pmEx, + "Error changing ACA DevID Certificate generation policy", + "Error updating policy. \n" + pmEx.getMessage()); + } + + // return the redirect + return redirectToSelf(new NoPageParams(), model, attr); + } + + /** + * Updates the state of the policy setting that indicates that the generation + * will occur in a set time frame and redirects + * back to the original page. + * + * @param ppModel The data posted by the form mapped into an object. + * @param attr RedirectAttributes used to forward data back to the original page. + * @return View containing the url and parameters + * @throws URISyntaxException if malformed URI + */ + @RequestMapping(value = "update-expire-on", method = RequestMethod.POST) + public RedirectView updateExpireOnVal(@ModelAttribute final PolicyPageModel ppModel, + final RedirectAttributes attr) + throws URISyntaxException { + + // set the data received to be populated back into the form + Map model = new HashMap<>(); + PageMessages messages = new PageMessages(); + String successMessage; + String numOfDays; + + boolean generateCertificateEnabled = false; + // because this is just one option, there is not 'unchecked' value, so it is either + // 'checked' or null + if (ppModel.getGenerationExpirationOn() != null) { + generateCertificateEnabled + = ppModel.getGenerationExpirationOn() + .equalsIgnoreCase(ENABLED_CHECKED_PARAMETER_VALUE); + } + + try { + SupplyChainSettings policy = getDefaultPolicyAndSetInModel(ppModel, model); + boolean issuedAttestationOptionEnabled + = policy.isIssueAttestationCertificate(); + + if (issuedAttestationOptionEnabled) { + if (generateCertificateEnabled) { + successMessage = "Attestation Certificate generation expiration time enabled."; + } else { + successMessage = "Attestation Certificate generation expiration time disabled."; + } + + if (generateCertificateEnabled) { + numOfDays = ppModel.getExpirationValue(); + if (numOfDays == null) { + numOfDays = SupplyChainSettings.TEN_YEARS; + } + } else { + numOfDays = policy.getValidityDays(); + } + + policy.setValidityDays(numOfDays); + } else { + generateCertificateEnabled = false; + successMessage = "Attestation Certificate generation is disabled, " + + "can not set time expiration"; + } + + policy.setGenerateOnExpiration(generateCertificateEnabled); + savePolicyAndApplySuccessMessage(ppModel, model, messages, successMessage, policy); + } catch (PolicyManagerException pmEx) { + handlePolicyManagerUpdateError(model, messages, pmEx, + "Error changing ACA Attestation Certificate generation policy", + "Error updating policy. \n" + pmEx.getMessage()); + } + + // return the redirect + return redirectToSelf(new NoPageParams(), model, attr); + } + + /** + * Updates the state of the policy setting that indicates that the generation + * will occur in a set time frame and redirects + * back to the original page. + * + * @param ppModel The data posted by the form mapped into an object. + * @param attr RedirectAttributes used to forward data back to the original page. + * @return View containing the url and parameters + * @throws URISyntaxException if malformed URI + */ + @RequestMapping(value = "update-devid-expire-on", method = RequestMethod.POST) + public RedirectView updateDevIdExpireOnVal(@ModelAttribute final PolicyPageModel ppModel, + final RedirectAttributes attr) + throws URISyntaxException { + + // set the data received to be populated back into the form + Map model = new HashMap<>(); + PageMessages messages = new PageMessages(); + String successMessage; + String numOfDays; + + boolean generateDevIdCertificateEnabled = false; + // because this is just one option, there is not 'unchecked' value, so it is either + // 'checked' or null + if (ppModel.getDevIdExpirationChecked() != null) { + generateDevIdCertificateEnabled + = ppModel.getDevIdExpirationChecked() + .equalsIgnoreCase(ENABLED_CHECKED_PARAMETER_VALUE); + } + + try { + SupplyChainSettings policy = getDefaultPolicyAndSetInModel(ppModel, model); + boolean issuedDevIdOptionEnabled + = policy.isIssueDevIdCertificate(); + + if (issuedDevIdOptionEnabled) { + if (generateDevIdCertificateEnabled) { + successMessage = "DevID Certificate generation expiration time enabled."; + } else { + successMessage = "DevID Certificate generation expiration time disabled."; + } + + if (generateDevIdCertificateEnabled) { + numOfDays = ppModel.getDevIdExpirationValue(); + if (numOfDays == null) { + numOfDays = SupplyChainSettings.TEN_YEARS; + } + } else { + numOfDays = policy.getDevIdValidityDays(); + } + + policy.setDevIdValidityDays(numOfDays); + } else { + generateDevIdCertificateEnabled = false; + successMessage = "DevID Certificate generation is disabled, " + + "can not set time expiration"; + } + + policy.setDevIdExpirationFlag(generateDevIdCertificateEnabled); + savePolicyAndApplySuccessMessage(ppModel, model, messages, successMessage, policy); + } catch (PolicyManagerException pmEx) { + handlePolicyManagerUpdateError(model, messages, pmEx, + "Error changing ACA DevID Certificate generation policy", + "Error updating policy. \n" + pmEx.getMessage()); + } + + // return the redirect + return redirectToSelf(new NoPageParams(), model, attr); + } + + /** + * Updates the state of the policy setting that indicates that the generation + * will occur in a set time frame from the end validity date and redirects + * back to the original page. + * + * @param ppModel The data posted by the form mapped into an object. + * @param attr RedirectAttributes used to forward data back to the original page. + * @return View containing the url and parameters + * @throws URISyntaxException if malformed URI + */ + @RequestMapping(value = "update-threshold", method = RequestMethod.POST) + public RedirectView updateThresholdVal(@ModelAttribute final PolicyPageModel ppModel, + final RedirectAttributes attr) + throws URISyntaxException { + + // set the data received to be populated back into the form + Map model = new HashMap<>(); + PageMessages messages = new PageMessages(); + String successMessage; + String threshold; + + boolean generateCertificateEnabled = false; + // because this is just one option, there is not 'unchecked' value, so it is either + // 'checked' or null + if (ppModel.getGenerationExpirationOn() != null) { + generateCertificateEnabled + = ppModel.getGenerationExpirationOn() + .equalsIgnoreCase(ENABLED_CHECKED_PARAMETER_VALUE); + } + + try { + SupplyChainSettings policy = getDefaultPolicyAndSetInModel(ppModel, model); + boolean issuedAttestationOptionEnabled + = policy.isIssueAttestationCertificate(); + + if (issuedAttestationOptionEnabled) { + if (generateCertificateEnabled) { + successMessage = "Attestation Certificate generation threshold time enabled."; + } else { + successMessage = "Attestation Certificate generation threshold time disabled."; + } + + if (generateCertificateEnabled) { + threshold = ppModel.getThresholdValue(); + } else { + threshold = ppModel.getReissueThreshold(); + } + + if (threshold == null || threshold.isEmpty()) { + threshold = SupplyChainSettings.YEAR; + } + + policy.setReissueThreshold(threshold); + } else { + generateCertificateEnabled = false; + successMessage = "Attestation Certificate generation is disabled, " + + "can not set time expiration"; + } + + policy.setGenerateOnExpiration(generateCertificateEnabled); + savePolicyAndApplySuccessMessage(ppModel, model, messages, successMessage, policy); + } catch (PolicyManagerException pmEx) { + handlePolicyManagerUpdateError(model, messages, pmEx, + "Error changing ACA Attestation Certificate generation policy", + "Error updating policy. \n" + pmEx.getMessage()); + } + + // return the redirect + return redirectToSelf(new NoPageParams(), model, attr); + } + + /** + * Updates the state of the policy setting that indicates that the generation + * will occur in a set time frame from the end validity date and redirects + * back to the original page. + * + * @param ppModel The data posted by the form mapped into an object. + * @param attr RedirectAttributes used to forward data back to the original page. + * @return View containing the url and parameters + * @throws URISyntaxException if malformed URI + */ + @RequestMapping(value = "update-devid-threshold", method = RequestMethod.POST) + public RedirectView updateDevIdThresholdVal(@ModelAttribute final PolicyPageModel ppModel, + final RedirectAttributes attr) + throws URISyntaxException { + // set the data received to be populated back into the form + Map model = new HashMap<>(); + PageMessages messages = new PageMessages(); + String successMessage; + String threshold; + + boolean generateDevIdCertificateEnabled = false; + // because this is just one option, there is not 'unchecked' value, so it is either + // 'checked' or null + if (ppModel.getDevIdExpirationChecked() != null) { + generateDevIdCertificateEnabled + = ppModel.getDevIdExpirationChecked() + .equalsIgnoreCase(ENABLED_CHECKED_PARAMETER_VALUE); + } + + try { + SupplyChainSettings policy = getDefaultPolicyAndSetInModel(ppModel, model); + boolean issuedDevIdOptionEnabled + = policy.isIssueDevIdCertificate(); + + if (issuedDevIdOptionEnabled) { + if (generateDevIdCertificateEnabled) { + successMessage = "DevID Certificate generation threshold time enabled."; + } else { + successMessage = "DevID Certificate generation threshold time disabled."; + } + + if (generateDevIdCertificateEnabled) { + threshold = ppModel.getDevIdThresholdValue(); + } else { + threshold = ppModel.getDevIdReissueThreshold(); + } + + if (threshold == null || threshold.isEmpty()) { + threshold = SupplyChainSettings.YEAR; + } + + policy.setDevIdReissueThreshold(threshold); + } else { + generateDevIdCertificateEnabled = false; + successMessage = "DevID Certificate generation is disabled, " + + "can not set time expiration"; + } + + policy.setDevIdExpirationFlag(generateDevIdCertificateEnabled); + savePolicyAndApplySuccessMessage(ppModel, model, messages, successMessage, policy); + } catch (PolicyManagerException pmEx) { + handlePolicyManagerUpdateError(model, messages, pmEx, + "Error changing ACA DevID Certificate generation policy", + "Error updating policy. \n" + pmEx.getMessage()); + } + + // return the redirect + return redirectToSelf(new NoPageParams(), model, attr); + } + + /** + * Updates the Endorsement Credential Validation policy setting and + * redirects back to the original page. + * + * @param ppModel The data posted by the form mapped into an object. + * @param attr RedirectAttributes used to forward data back to the original + * page. + * @return View containing the url and parameters + * @throws URISyntaxException if malformed URI + */ + @RequestMapping(value = "update-ec-validation", method = RequestMethod.POST) + public RedirectView updateEcVal(@ModelAttribute final PolicyPageModel ppModel, + final RedirectAttributes attr) throws URISyntaxException { + + // set the data received to be populated back into the form + Map model = new HashMap<>(); + PageMessages messages = new PageMessages(); + String successMessage; + boolean ecValidationOptionEnabled + = ppModel.getEcValidate().equalsIgnoreCase(ENABLED_CHECKED_PARAMETER_VALUE); + + try { + SupplyChainSettings policy = getDefaultPolicyAndSetInModel(ppModel, model); + + //If PC Validation is enabled without EC Validation, disallow change + if (!isPolicyValid(ecValidationOptionEnabled, policy.isPcValidationEnabled(), + policy.isPcAttributeValidationEnabled())) { + handleUserError(model, messages, + "To disable Endorsement Credential Validation, Platform Validation" + + " must also be disabled."); + return redirectToSelf(new NoPageParams(), model, attr); + } + // set the policy option and create success message + if (ecValidationOptionEnabled) { + policy.setEcValidationEnabled(true); + successMessage = "Endorsement credential validation enabled"; + } else { + policy.setEcValidationEnabled(false); + successMessage = "Endorsement credential validation disabled"; + } + + savePolicyAndApplySuccessMessage(ppModel, model, messages, successMessage, policy); + } catch (PolicyManagerException pmEx) { + handlePolicyManagerUpdateError(model, messages, pmEx, + "Error changing ACA endorsement validation policy", + "Error updating policy. \n" + pmEx.getMessage()); + } + + // return the redirect + return redirectToSelf(new NoPageParams(), model, attr); + } + + /** + * Updates the Firmware Validation policy setting and + * redirects back to the original page. + * + * @param ppModel The data posted by the form mapped into an object. + * @param attr RedirectAttributes used to forward data back to the original + * page. + * @return View containing the url and parameters + * @throws URISyntaxException if malformed URI + */ + @RequestMapping(value = "update-firmware-validation", method = RequestMethod.POST) + public RedirectView updateFirmwareVal(@ModelAttribute final PolicyPageModel ppModel, + final RedirectAttributes attr) throws URISyntaxException { + + // set the data received to be populated back into the form + Map model = new HashMap<>(); + PageMessages messages = new PageMessages(); + String successMessage; + boolean firmwareValidationOptionEnabled = ppModel.getFmValidate() + .equalsIgnoreCase(ENABLED_CHECKED_PARAMETER_VALUE); + + try { + SupplyChainSettings policy = getDefaultPolicyAndSetInModel(ppModel, model); + + //If firmware is enabled without PC attributes, disallow change + if (firmwareValidationOptionEnabled && !policy.isPcAttributeValidationEnabled()) { + handleUserError(model, messages, + "Firmware validation can not be " + + "enabled without PC Attributes policy enabled."); + return redirectToSelf(new NoPageParams(), model, attr); + } + + // set the policy option and create success message + if (firmwareValidationOptionEnabled) { + policy.setFirmwareValidationEnabled(true); + policy.setIgnoreGptEnabled(true); + successMessage = "Firmware validation enabled"; + } else { + policy.setFirmwareValidationEnabled(false); + policy.setIgnoreImaEnabled(false); + policy.setIgnoretBootEnabled(false); + policy.setIgnoreOsEvtEnabled(false); + successMessage = "Firmware validation disabled"; + } + + savePolicyAndApplySuccessMessage(ppModel, model, messages, successMessage, policy); + } catch (PolicyManagerException pmEx) { + handlePolicyManagerUpdateError(model, messages, pmEx, + "Error changing ACA firmware validation policy", + "Error updating policy. \n" + pmEx.getMessage()); + + } + + // return the redirect + return redirectToSelf(new NoPageParams(), model, attr); + } + + /** + * Updates the ignore IMA policy setting and + * redirects back to the original page. + * + * @param ppModel The data posted by the form mapped into an object. + * @param attr RedirectAttributes used to forward data back to the original + * page. + * @return View containing the url and parameters + * @throws URISyntaxException if malformed URI + */ + @RequestMapping(value = "update-ima-ignore", method = RequestMethod.POST) + public RedirectView updateIgnoreIma(@ModelAttribute final PolicyPageModel ppModel, + final RedirectAttributes attr) throws URISyntaxException { + // set the data received to be populated back into the form + Map model = new HashMap<>(); + PageMessages messages = new PageMessages(); + String successMessage; + boolean ignoreImaOptionEnabled = ppModel.getIgnoreIma() + .equalsIgnoreCase(ENABLED_CHECKED_PARAMETER_VALUE); + + try { + SupplyChainSettings policy = getDefaultPolicyAndSetInModel(ppModel, model); + + //If Ignore IMA is enabled without firmware, disallow change + if (ignoreImaOptionEnabled && !policy.isFirmwareValidationEnabled()) { + handleUserError(model, messages, + "Ignore IMA can not be " + + "enabled without Firmware Validation policy enabled."); + return redirectToSelf(new NoPageParams(), model, attr); + } + + // set the policy option and create success message + if (ignoreImaOptionEnabled) { + policy.setIgnoreImaEnabled(true); + successMessage = "Ignore IMA enabled"; + } else { + policy.setIgnoreImaEnabled(false); + successMessage = "Ignore IMA disabled"; + } + + savePolicyAndApplySuccessMessage(ppModel, model, messages, successMessage, policy); + } catch (PolicyManagerException pmEx) { + handlePolicyManagerUpdateError(model, messages, pmEx, + "Error changing ACA IMA ignore policy", + "Error updating policy. \n" + pmEx.getMessage()); + } + + // return the redirect + return redirectToSelf(new NoPageParams(), model, attr); + } + + /** + * Updates the ignore TBoot policy setting and + * redirects back to the original page. + * + * @param ppModel The data posted by the form mapped into an object. + * @param attr RedirectAttributes used to forward data back to the original + * page. + * @return View containing the url and parameters + * @throws URISyntaxException if malformed URI + */ + @RequestMapping(value = "update-tboot-ignore", method = RequestMethod.POST) + public RedirectView updateIgnoreTboot(@ModelAttribute final PolicyPageModel ppModel, + final RedirectAttributes attr) throws URISyntaxException { + // set the data received to be populated back into the form + Map model = new HashMap<>(); + PageMessages messages = new PageMessages(); + String successMessage; + boolean ignoreTbootOptionEnabled = ppModel.getIgnoretBoot() + .equalsIgnoreCase(ENABLED_CHECKED_PARAMETER_VALUE); + + try { + SupplyChainSettings policy = getDefaultPolicyAndSetInModel(ppModel, model); + + //If Ignore TBoot is enabled without firmware, disallow change + if (ignoreTbootOptionEnabled && !policy.isFirmwareValidationEnabled()) { + handleUserError(model, messages, + "Ignore TBoot can not be " + + "enabled without Firmware Validation policy enabled."); + return redirectToSelf(new NoPageParams(), model, attr); + } + + // set the policy option and create success message + if (ignoreTbootOptionEnabled) { + policy.setIgnoretBootEnabled(true); + successMessage = "Ignore TBoot enabled"; + } else { + policy.setIgnoretBootEnabled(false); + successMessage = "Ignore TBoot disabled"; + } + + savePolicyAndApplySuccessMessage(ppModel, model, messages, successMessage, policy); + } catch (PolicyManagerException pmEx) { + handlePolicyManagerUpdateError(model, messages, pmEx, + "Error changing ACA TBoot ignore policy", + "Error updating policy. \n" + pmEx.getMessage()); + } + + // return the redirect + return redirectToSelf(new NoPageParams(), model, attr); + } + + /** + * Updates the ignore GPT policy setting and + * redirects back to the original page. + * + * @param ppModel The data posted by the form mapped into an object. + * @param attr RedirectAttributes used to forward data back to the original + * page. + * @return View containing the url and parameters + * @throws URISyntaxException if malformed URI + */ + @RequestMapping(value = "update-gpt-ignore", method = RequestMethod.POST) + public RedirectView updateIgnoreGptEvents(@ModelAttribute final PolicyPageModel ppModel, + final RedirectAttributes attr) throws URISyntaxException { + // set the data received to be populated back into the form + Map model = new HashMap<>(); + PageMessages messages = new PageMessages(); + String successMessage; + boolean ignoreGptOptionEnabled = ppModel.getIgnoreGpt() + .equalsIgnoreCase(ENABLED_CHECKED_PARAMETER_VALUE); + + try { + SupplyChainSettings policy = getDefaultPolicyAndSetInModel(ppModel, model); + + //If Ignore TBoot is enabled without firmware, disallow change + if (ignoreGptOptionEnabled && !policy.isFirmwareValidationEnabled()) { + handleUserError(model, messages, + "Ignore GPT Events can not be " + + "enabled without Firmware Validation policy enabled."); + return redirectToSelf(new NoPageParams(), model, attr); + } + + // set the policy option and create success message + if (ignoreGptOptionEnabled) { + policy.setIgnoreGptEnabled(true); + successMessage = "Ignore GPT enabled"; + } else { + policy.setIgnoreGptEnabled(false); + successMessage = "Ignore GPT disabled"; + } + + savePolicyAndApplySuccessMessage(ppModel, model, messages, successMessage, policy); + } catch (PolicyManagerException pmEx) { + handlePolicyManagerUpdateError(model, messages, pmEx, + "Error changing ACA GPT ignore policy", + "Error updating policy. \n" + pmEx.getMessage()); + } + + // return the redirect + return redirectToSelf(new NoPageParams(), model, attr); + } + + /** + * Updates the ignore Os Events policy setting and + * redirects back to the original page. + * + * @param ppModel The data posted by the form mapped into an object. + * @param attr RedirectAttributes used to forward data back to the original + * page. + * @return View containing the url and parameters + * @throws URISyntaxException if malformed URI + */ + @RequestMapping(value = "update-os-evt-ignore", method = RequestMethod.POST) + public RedirectView updateIgnoreOsEvents( + @ModelAttribute final PolicyPageModel ppModel, + final RedirectAttributes attr) + throws URISyntaxException { + // set the data received to be populated back into the form + Map model = new HashMap<>(); + PageMessages messages = new PageMessages(); + String successMessage; + boolean ignoreOsEvtOptionEnabled = ppModel.getIgnoreOsEvt() + .equalsIgnoreCase(ENABLED_CHECKED_PARAMETER_VALUE); + + try { + SupplyChainSettings policy = getDefaultPolicyAndSetInModel(ppModel, model); + + //If Ignore TBoot is enabled without firmware, disallow change + if (ignoreOsEvtOptionEnabled && !policy.isFirmwareValidationEnabled()) { + handleUserError(model, messages, + "Ignore Os Events can not be " + + "enabled without Firmware Validation policy enabled."); + return redirectToSelf(new NoPageParams(), model, attr); + } + + // set the policy option and create success message + if (ignoreOsEvtOptionEnabled) { + policy.setIgnoreOsEvtEnabled(true); + policy.setIgnoreGptEnabled(true); + successMessage = "Ignore OS Events enabled"; + } else { + policy.setIgnoreOsEvtEnabled(false); + successMessage = "Ignore OS Events disabled"; + } + + savePolicyAndApplySuccessMessage(ppModel, model, messages, successMessage, policy); + } catch (PolicyManagerException pmEx) { + handlePolicyManagerUpdateError(model, messages, pmEx, + "Error changing ACA OS Events ignore policy", + "Error updating policy. \n" + pmEx.getMessage()); + } + + // return the redirect + return redirectToSelf(new NoPageParams(), model, attr); + } + + private void handlePolicyManagerUpdateError(final Map model, + final PageMessages messages, + final PolicyManagerException pmEx, + final String message, final String error) { + log.error(message, pmEx); + messages.addError(error); + model.put(MESSAGES_ATTRIBUTE, messages); + } + + private void handleUserError(final Map model, + final PageMessages messages, + final String errorMessage) { + messages.addError(errorMessage); + model.put(MESSAGES_ATTRIBUTE, messages); + } + + /** + * Takes in policy setting states and determines if policy configuration is + * valid or not. PC Attribute Validation must have PC Validation Enabled PC + * Validation must have EC Validation enabled + * + * @param isEcEnable EC Validation Policy State + * @param isPcEnable PC Validation Policy State + * @param isPcAttEnable PC Attribute Validation Policy State + * @return True if policy combination is valid + */ + private static boolean isPolicyValid(final boolean isEcEnable, final boolean isPcEnable, + final boolean isPcAttEnable) { + if (isPcAttEnable && !isPcEnable) { + return false; + } else { + return !isPcEnable || isEcEnable; + } + } + + /** + * Helper function to get a fresh load of the default policy from the DB. + * + * @return The default Supply Chain Policy + */ + private SupplyChainSettings getDefaultPolicy() { + SupplyChainSettings defaultSettings = this.settingsService.getByName("Default"); + + if (defaultSettings == null) { + defaultSettings = new SupplyChainSettings("Default", "Settings are configured for no validation flags set."); + } + return defaultSettings; + } + + /** + * Gets the default policy and applies the current values in to the page + * model. + * + * @param ppModel the page model + * @param model the map of string messages to be displayed on the view + * @return The default Supply Chain Policy + */ + private SupplyChainSettings getDefaultPolicyAndSetInModel( + final PolicyPageModel ppModel, final Map model) { + // load the current default policy from the DB + SupplyChainSettings policy = getDefaultPolicy(); + + // set the data received to be populated back into the form + model.put(RESULT_DATA, ppModel); + return policy; + } + + private void savePolicyAndApplySuccessMessage( + final PolicyPageModel ppModel, final Map model, + final PageMessages messages, final String successMessage, + final SupplyChainSettings settings) { + // save the policy to the DB + settingsService.updateSettings(settings); + + // Log and set the success message + messages.addSuccess(successMessage); + log.debug("ACA Policy set to: " + ppModel.toString()); + + model.put(MESSAGES_ATTRIBUTE, messages); + } +} + diff --git a/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/params/CertificateDetailsPageParams.java b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/params/CertificateDetailsPageParams.java new file mode 100644 index 00000000..c4c07382 --- /dev/null +++ b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/params/CertificateDetailsPageParams.java @@ -0,0 +1,59 @@ +package hirs.attestationca.portal.page.params; + +import hirs.attestationca.portal.page.PageParams; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.Setter; + +import java.util.LinkedHashMap; + +/** + * URL parameters object for the CertificateDetails page and controller. + */ +@Getter +@Setter +@AllArgsConstructor +public class CertificateDetailsPageParams implements PageParams { + + private String id; + private String type; + + /** + * Constructor to set ID Certificate Details URL parameters. + * + * @param id the String parameter to set + */ + public CertificateDetailsPageParams(final String id) { + this.id = id; + } + + /** + * Default constructor for Spring. + */ + public CertificateDetailsPageParams() { + id = null; + type = null; + } + + /** + * Allows PageController to iterate over the url parameters. + * + * @return map containing the object's URL parameters. + */ + @Override + public LinkedHashMap asMap() { + LinkedHashMap map = new LinkedHashMap<>(); + map.put("id", id); + map.put("type", type); + return map; + } + + @Override + public String toString() { + return "CertificateDetailsPageParams{" + + "id:' " + id + "'," + + "type: " + type + + "}"; + } + +} diff --git a/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/params/NoPageParams.java b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/params/NoPageParams.java new file mode 100644 index 00000000..55497f98 --- /dev/null +++ b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/params/NoPageParams.java @@ -0,0 +1,21 @@ +package hirs.attestationca.portal.page.params; + +import hirs.attestationca.portal.page.PageParams; +import java.util.LinkedHashMap; + +/** + * Minimal implementation of PageParams for pages that do not have url parameters. + */ +public class NoPageParams implements PageParams { + + /** + * Returns empty map so when iteration is required, nothing happens. + * + * @return empty map. + */ + @Override + public LinkedHashMap asMap() { + return new LinkedHashMap<>(); + } + +} diff --git a/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/params/ReferenceManifestDetailsPageParams.java b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/params/ReferenceManifestDetailsPageParams.java new file mode 100644 index 00000000..23a3ddf6 --- /dev/null +++ b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/params/ReferenceManifestDetailsPageParams.java @@ -0,0 +1,39 @@ +package hirs.attestationca.portal.page.params; + +import hirs.attestationca.portal.page.PageParams; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.util.LinkedHashMap; + +/** + * URL parameters object for the Reference Manifest Details page and controller. + */ +@NoArgsConstructor +@AllArgsConstructor +public class ReferenceManifestDetailsPageParams implements PageParams { + + @Getter @Setter + private String id; + + /** + * Allows PageController to iterate over the url parameters. + * + * @return map containing the object's URL parameters. + */ + @Override + public LinkedHashMap asMap() { + LinkedHashMap map = new LinkedHashMap<>(); + map.put("id", id); + return map; + } + + @Override + public String toString() { + return "ReferenceManifestDetailsPageParams{" + + "id:' " + id + + "}"; + } +} diff --git a/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/params/ReferenceManifestPageParams.java b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/params/ReferenceManifestPageParams.java new file mode 100644 index 00000000..789bd5fc --- /dev/null +++ b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/params/ReferenceManifestPageParams.java @@ -0,0 +1,44 @@ +package hirs.attestationca.portal.page.params; + +import hirs.attestationca.portal.page.PageParams; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.util.LinkedHashMap; + +/** + * URL parameters object for the ReferenceManifest page and controller. + */ +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +public class ReferenceManifestPageParams implements PageParams { + + private String id; + private String type; + + /** + *Constructor to set all Reference Integrity Manifest URL parameters. + * + * @param id the String parameter to set + */ + public ReferenceManifestPageParams(final String id) { + this.id = id; + } + + /** + * Allows PageController to iterate over the url parameters. + * + * @return map containing the object's URL parameters. + */ + @Override + public LinkedHashMap asMap() { + LinkedHashMap map = new LinkedHashMap<>(); + map.put("id", id); + map.put("type", type); + return map; + } +} diff --git a/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/params/package-info.java b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/params/package-info.java new file mode 100644 index 00000000..ab35d472 --- /dev/null +++ b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/params/package-info.java @@ -0,0 +1 @@ +package hirs.attestationca.portal.page.params; \ No newline at end of file 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 new file mode 100644 index 00000000..b5b84130 --- /dev/null +++ b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/utils/CertificateStringMapBuilder.java @@ -0,0 +1,170 @@ +package hirs.attestationca.portal.page.utils; + +import hirs.attestationca.persist.entity.userdefined.Certificate; +import hirs.attestationca.persist.entity.userdefined.certificate.CertificateAuthorityCredential; +import hirs.attestationca.persist.service.CertificateServiceImpl; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import lombok.extern.log4j.Log4j2; + +import java.io.IOException; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + +/** + * Utility class for mapping certificate information in to string maps. These are used to display + * information on a web page, as X509 cert classes do not serialize to JSON + */ +@Log4j2 +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public final class CertificateStringMapBuilder { + + /** + * Returns the general information. + * + * @param certificate certificate to get the general information. + * @param certificateServiceImpl the certificate manager for retrieving certs. + * @return a hash map with the general certificate information. + */ + public static HashMap getGeneralCertificateInfo( + final Certificate certificate, final CertificateServiceImpl certificateServiceImpl) { + HashMap data = new HashMap<>(); + + return data; + } + + /** + * Recursive function that check if all the certificate chain is present. + * + * @param certificate certificate to get the issuer + * @param certificateServiceImpl the certificate manager for retrieving certs. + * @return a boolean indicating if it has the full chain or not. + */ + public static Certificate containsAllChain( + final Certificate certificate, + final CertificateServiceImpl certificateServiceImpl) { + Set issuerCertificates = new HashSet<>(); + CertificateAuthorityCredential skiCA = null; + String issuerResult; + + return null; + } + + /** + * Returns the Certificate Authority information. + * + * @param uuid ID for the certificate. + * @param certificateServiceImpl the certificate manager for retrieving certs. + * @return a hash map with the endorsement certificate information. + */ + public static HashMap getCertificateAuthorityInformation(final UUID uuid, + final CertificateServiceImpl certificateServiceImpl) { +// CertificateAuthorityCredential certificate = +// CertificateAuthorityCredential +// .select(certificateManager) +// .byEntityId(uuid) +// .getCertificate(); + String notFoundMessage = "Unable to find Certificate Authority " + + "Credential with ID: " + uuid; + +// return getCertificateAuthorityInfoHelper(certificateServiceImpl, certificate, notFoundMessage); + return null; + } + + /** + * Returns the Trust Chain credential information. + * + * @param certificate the certificate + * @param certificateServiceImpl the certificate manager for retrieving certs. + * @return a hash map with the endorsement certificate information. + */ + public static HashMap getCertificateAuthorityInformation( + final CertificateAuthorityCredential certificate, + final CertificateServiceImpl certificateServiceImpl) { +// return getCertificateAuthorityInfoHelper(certificateManager, certificate, +// "No cert provided for mapping"); + return null; + } + + private static HashMap getCertificateAuthorityInfoHelper( + final CertificateServiceImpl certificateServiceImpl, + final CertificateAuthorityCredential certificate, final String notFoundMessage) { + HashMap data = new HashMap<>(); + + return data; + } + + /** + * Returns the endorsement credential information. + * + * @param uuid ID for the certificate. + * @param certificateServiceImpl the certificate manager for retrieving certs. + * @return a hash map with the endorsement certificate information. + */ + public static HashMap getEndorsementInformation(final UUID uuid, + final CertificateServiceImpl certificateServiceImpl) { + HashMap data = new HashMap<>(); + + return data; + } + + /** + * Returns the Platform credential information. + * + * @param uuid ID for the certificate. + * @param certificateServiceImpl the certificate manager for retrieving certs. + * @return a hash map with the endorsement certificate information. + * @throws IOException when parsing the certificate + * @throws IllegalArgumentException invalid argument on parsing the certificate + */ + public static HashMap getPlatformInformation(final UUID uuid, + final CertificateServiceImpl certificateServiceImpl) + throws IllegalArgumentException, IOException { + HashMap data = new HashMap<>(); + + return data; + } + + /** + * Returns a HasHMap of a string. + * Ex: input "TPMSpecification{family='abc',level=0, revision=0}" + * output map[TPMSpecificationFamily] = 'abc' + * map[TPMSpecificationLevel] = 0 + * map[TPMSpecificationRevision] = 0 + * + * @param str HashMap string to be converted. + * @return a hash map with key-value pairs from the string + */ + private static HashMap convertStringToHash(final String str) { + HashMap map = new HashMap<>(); + String name = str.substring(0, str.indexOf('{')).trim(); + String data = str.trim().substring(str.trim().indexOf('{') + 1, + str.trim().length() - 1); + // Separate key and value and parse the key + for (String pair : data.split(",")) { + String[] keyValue = pair.split("="); + // Remove white space and change first character in the key to uppercase + keyValue[0] = Character.toUpperCase( + keyValue[0].trim().charAt(0)) + keyValue[0].trim().substring(1); + + map.put(name + keyValue[0], keyValue[1].trim()); + } + return map; + } + + /** + * Returns the Issued Attestation Certificate information. + * + * @param uuid ID for the certificate. + * @param certificateServiceImpl the certificate manager for retrieving certs. + * @return a hash map with the endorsement certificate information. + */ + public static HashMap getIssuedInformation(final UUID uuid, + final CertificateServiceImpl certificateServiceImpl) { + HashMap data = new HashMap<>(); + + return data; + } +} diff --git a/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/utils/PciIds.java b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/utils/PciIds.java new file mode 100644 index 00000000..3623e922 --- /dev/null +++ b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/utils/PciIds.java @@ -0,0 +1,191 @@ +package hirs.attestationca.portal.page.utils; + +import com.github.marandus.pciid.model.Device; +import com.github.marandus.pciid.model.Vendor; +import com.github.marandus.pciid.service.PciIdsDatabase; +import com.google.common.base.Strings; +import hirs.attestationca.persist.entity.userdefined.certificate.attributes.ComponentIdentifier; +import hirs.attestationca.persist.entity.userdefined.certificate.attributes.V2.ComponentIdentifierV2; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import org.bouncycastle.asn1.DERUTF8String; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Provide Java access to PCI IDs. + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public final class PciIds { + /** + * This pci ids file can be in different places on different distributions. + */ + public static final List PCI_IDS_PATH = + Collections.unmodifiableList(new ArrayList<>() { + private static final long serialVersionUID = 1L; + { + add("/usr/share/hwdata/pci.ids"); + add("/usr/share/misc/pci.ids"); + add("/tmp/pci.ids"); + } + }); + + /** + * The PCI IDs Database object. + * + * This only needs to be loaded one time. + * + * The pci ids library protects the data inside the object by making it immutable. + */ + public static final PciIdsDatabase DB = new PciIdsDatabase(); + + static { + if (!DB.isReady()) { + String dbFile = null; + for (final String path : PCI_IDS_PATH) { + if ((new File(path)).exists()) { + dbFile = path; + break; + } + } + if (dbFile != null) { + InputStream is = null; + try { + is = new FileInputStream(new File(dbFile)); + DB.loadStream(is); + } catch (IOException e) { + // DB will not be ready, hardware IDs will not be translated + dbFile = null; + } finally { + if (is != null) { + try { + is.close(); + } catch (IOException e) { + dbFile = null; + } + } + } + } + } + } + + /** + * The Component Class TCG Registry OID. + */ + public static final String COMPCLASS_TCG_OID = "2.23.133.18.3.1"; + /** + * The Component Class Value mask for NICs. + */ + public static final String COMPCLASS_TCG_CAT_NIC = "00090000"; + /** + * The Component Class Value mask for GFX cards. + */ + public static final String COMPCLASS_TCG_CAT_GFX = "00050000"; + + /** + * Iterate through all components and translate PCI hardware IDs as necessary. It will only + * translate ComponentIdentifierV2+ objects as it relies on Component Class information. + * @param components List of ComponentIdentifiers. + * @return the translated list of ComponentIdentifiers. + */ + public static List translate( + final List components) { + List newList = new ArrayList<>(); + if (components != null && !components.isEmpty()) { + for (final ComponentIdentifier component : components) { + // V2 components should not be found alongside V1 components + // they pass through just in case + if (component.isVersion2()) { + newList.add(translate((ComponentIdentifierV2) component)); + } else { + newList.add(component); + } + } + } + return newList; + } + + /** + * Translate Vendor and Device IDs, if found, in ComponentIdentifierV2 objects. + * It will only translate ID values, any other value will pass through. + * @param component ComponentIdentifierV2 object. + * @return the translated ComponentIdentifierV2 object. + */ + public static ComponentIdentifierV2 translate(final ComponentIdentifierV2 component) { + ComponentIdentifierV2 newComponent = null; + if (component != null) { + newComponent = component; + // This can be updated as we get more accurate component class registries and values + // Component Class Registry not accessible: TCG assumed + final String compClassValue = component.getComponentClass().getCategory(); + if (compClassValue.equals(COMPCLASS_TCG_CAT_NIC) + || compClassValue.equals(COMPCLASS_TCG_CAT_GFX)) { + DERUTF8String manufacturer = translateVendor(component.getComponentManufacturer()); + DERUTF8String model = translateDevice(component.getComponentManufacturer(), + component.getComponentModel()); + + newComponent = new ComponentIdentifierV2(component.getComponentClass(), + manufacturer, + model, + component.getComponentSerial(), + component.getComponentRevision(), + component.getComponentManufacturerId(), + component.getFieldReplaceable(), + component.getComponentAddress(), + component.getCertificateIdentifier(), + component.getComponentPlatformUri(), + component.getAttributeStatus()); + } + + } + return newComponent; + } + + /** + * Look up the vendor name from the PCI IDs list, if the input string contains an ID. + * If any part of this fails, return the original manufacturer value. + * @param refManufacturer DERUTF8String, likely from a ComponentIdentifier + * @return DERUTF8String with the discovered vendor name, or the original manufacturer value. + */ + public static DERUTF8String translateVendor(final DERUTF8String refManufacturer) { + DERUTF8String manufacturer = refManufacturer; + if (manufacturer != null && manufacturer.getString().trim().matches("^[0-9A-Fa-f]{4}$")) { + Vendor ven = DB.findVendor(manufacturer.getString().toLowerCase()); + if (ven != null && !Strings.isNullOrEmpty(ven.getName())) { + manufacturer = new DERUTF8String(ven.getName()); + } + } + return manufacturer; + } + + /** + * Look up the device name from the PCI IDs list, if the input strings contain IDs. + * The Device lookup requires the Vendor ID AND the Device ID to be valid values. + * If any part of this fails, return the original model value. + * @param refManufacturer DERUTF8String, likely from a ComponentIdentifier + * @param refModel DERUTF8String, likely from a ComponentIdentifier + * @return DERUTF8String with the discovered device name, or the original model value. + */ + public static DERUTF8String translateDevice(final DERUTF8String refManufacturer, + final DERUTF8String refModel) { + DERUTF8String manufacturer = refManufacturer; + DERUTF8String model = refModel; + if (manufacturer != null + && model != null + && manufacturer.getString().trim().matches("^[0-9A-Fa-f]{4}$") + && model.getString().trim().matches("^[0-9A-Fa-f]{4}$")) { + Device dev = DB.findDevice(manufacturer.getString().toLowerCase(), + model.getString().toLowerCase()); + if (dev != null && !Strings.isNullOrEmpty(dev.getName())) { + model = new DERUTF8String(dev.getName()); + } + } + return model; + } +} diff --git a/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/utils/package-info.java b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/utils/package-info.java new file mode 100644 index 00000000..cc0dc317 --- /dev/null +++ b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/utils/package-info.java @@ -0,0 +1 @@ +package hirs.attestationca.portal.page.utils; \ No newline at end of file diff --git a/HIRS_AttestationCAPortal/src/main/resources/log4j2-spring.xml b/HIRS_AttestationCAPortal/src/main/resources/log4j2-spring.xml new file mode 100644 index 00000000..5c4aadef --- /dev/null +++ b/HIRS_AttestationCAPortal/src/main/resources/log4j2-spring.xml @@ -0,0 +1,35 @@ + + + + + + + + + %d{yyyy-MM-dd HH:mm:ss} [%C.%M] %-5p : %m%n + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file