From 4dd395b988d74731f6477976d9a344019b5359d7 Mon Sep 17 00:00:00 2001
From: iadgovuser62 <iadgovuser62@empire.eclipse.ncsc.mil>
Date: Fri, 16 Feb 2024 10:19:48 -0500
Subject: [PATCH 1/3] Adding PolicySettingsTest

---
 .../userdefined/PolicySettingsTest.java       | 54 +++++++++++++++++++
 1 file changed, 54 insertions(+)
 create mode 100644 HIRS_AttestationCA/src/test/java/hirs/attestationca/persist/entity/userdefined/PolicySettingsTest.java

diff --git a/HIRS_AttestationCA/src/test/java/hirs/attestationca/persist/entity/userdefined/PolicySettingsTest.java b/HIRS_AttestationCA/src/test/java/hirs/attestationca/persist/entity/userdefined/PolicySettingsTest.java
new file mode 100644
index 00000000..1182ac2e
--- /dev/null
+++ b/HIRS_AttestationCA/src/test/java/hirs/attestationca/persist/entity/userdefined/PolicySettingsTest.java
@@ -0,0 +1,54 @@
+package hirs.attestationca.persist.entity.userdefined;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Unit test class for PolicySettings.
+ */
+public class PolicySettingsTest {
+
+    /**
+     * Tests that default policy settings are set correctly.
+     */
+    @Test
+    public final void checkDefaultSettings() {
+        PolicySettings policy = new PolicySettings("Default Supply Chain Policy");
+        assertFalse(policy.isEcValidationEnabled());
+        assertFalse(policy.isPcValidationEnabled());
+        assertFalse(policy.isPcAttributeValidationEnabled());
+        assertFalse(policy.isExpiredCertificateValidationEnabled());
+        assertFalse(policy.isReplaceEC());
+    }
+
+    /**
+     * Tests that all setters and getters work.
+     */
+    @Test
+    public final void flipDefaultSettings() {
+        PolicySettings policy = new PolicySettings("Default Supply Chain Policy");
+        policy.setEcValidationEnabled(false);
+        policy.setPcValidationEnabled(false);
+        policy.setPcAttributeValidationEnabled(false);
+        policy.setExpiredCertificateValidationEnabled(false);
+        policy.setReplaceEC(true);
+        assertFalse(policy.isEcValidationEnabled());
+        assertFalse(policy.isPcValidationEnabled());
+        assertFalse(policy.isPcAttributeValidationEnabled());
+        assertFalse(policy.isExpiredCertificateValidationEnabled());
+        assertTrue(policy.isReplaceEC());
+    }
+
+    /**
+     * Tests that we can initiate a policy with a description.
+     */
+    @Test
+    public final void createPolicyWithDescription() {
+        final String description = "A default policy";
+        PolicySettings policy = new PolicySettings("Default Supply Chain Policy",
+                description);
+        assertEquals(policy.getDescription(), description);
+    }
+}

From 42a05a9e00eda1f3e8833c401efe2f46351217f0 Mon Sep 17 00:00:00 2001
From: iadgovuser62 <iadgovuser62@empire.eclipse.ncsc.mil>
Date: Tue, 20 Feb 2024 09:37:22 -0500
Subject: [PATCH 2/3] Adding SupplyChainCredentialValidatorTest, fixing
 assertEquals syntax in PolicySettingsTest, un-privating method in
 CertificateAttributeScvValidator

---
 .../CertificateAttributeScvValidator.java     |    6 +-
 .../userdefined/PolicySettingsTest.java       |    2 +-
 .../SupplyChainCredentialValidatorTest.java   | 2005 +++++++++++++++++
 .../hirs/validation/sample_paccor_output.txt  |   36 +
 ...ple_paccor_output_not_specified_values.txt |   34 +
 ...ple_paccor_output_with_extra_component.txt |   30 +
 .../platform_credentials/Intel_pc2.pem        |  Bin 0 -> 914 bytes
 .../pciids_plat_cert_2-0.pem                  |   37 +
 8 files changed, 2146 insertions(+), 4 deletions(-)
 create mode 100644 HIRS_AttestationCA/src/test/java/hirs/attestationca/persist/validation/SupplyChainCredentialValidatorTest.java
 create mode 100755 HIRS_AttestationCA/src/test/resources/hirs/validation/sample_paccor_output.txt
 create mode 100755 HIRS_AttestationCA/src/test/resources/hirs/validation/sample_paccor_output_not_specified_values.txt
 create mode 100755 HIRS_AttestationCA/src/test/resources/hirs/validation/sample_paccor_output_with_extra_component.txt
 create mode 100644 HIRS_AttestationCA/src/test/resources/validation/platform_credentials/Intel_pc2.pem
 create mode 100644 HIRS_AttestationCA/src/test/resources/validation/platform_credentials/pciids_plat_cert_2-0.pem

diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/validation/CertificateAttributeScvValidator.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/validation/CertificateAttributeScvValidator.java
index b2eb6d0c..45efbdc8 100644
--- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/validation/CertificateAttributeScvValidator.java
+++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/validation/CertificateAttributeScvValidator.java
@@ -877,9 +877,9 @@ public class CertificateAttributeScvValidator extends SupplyChainCredentialValid
      * @param potentialMatch the component info from a device info report
      * @return true if the fields match exactly (null is considered the same as an empty string)
      */
-    private static boolean isMatch(final UUID certificateId,
-                                   final ComponentIdentifier pcComponent,
-                                   final ComponentInfo potentialMatch) {
+    public static boolean isMatch(final UUID certificateId,
+                                  final ComponentIdentifier pcComponent,
+                                  final ComponentInfo potentialMatch) {
         boolean matchesSoFar = true;
 
         matchesSoFar &= isMatchOrEmptyInPlatformCert(
diff --git a/HIRS_AttestationCA/src/test/java/hirs/attestationca/persist/entity/userdefined/PolicySettingsTest.java b/HIRS_AttestationCA/src/test/java/hirs/attestationca/persist/entity/userdefined/PolicySettingsTest.java
index 1182ac2e..dfd56a55 100644
--- a/HIRS_AttestationCA/src/test/java/hirs/attestationca/persist/entity/userdefined/PolicySettingsTest.java
+++ b/HIRS_AttestationCA/src/test/java/hirs/attestationca/persist/entity/userdefined/PolicySettingsTest.java
@@ -49,6 +49,6 @@ public class PolicySettingsTest {
         final String description = "A default policy";
         PolicySettings policy = new PolicySettings("Default Supply Chain Policy",
                 description);
-        assertEquals(policy.getDescription(), description);
+        assertEquals(description, policy.getDescription());
     }
 }
diff --git a/HIRS_AttestationCA/src/test/java/hirs/attestationca/persist/validation/SupplyChainCredentialValidatorTest.java b/HIRS_AttestationCA/src/test/java/hirs/attestationca/persist/validation/SupplyChainCredentialValidatorTest.java
new file mode 100644
index 00000000..16a5dd2c
--- /dev/null
+++ b/HIRS_AttestationCA/src/test/java/hirs/attestationca/persist/validation/SupplyChainCredentialValidatorTest.java
@@ -0,0 +1,2005 @@
+package hirs.attestationca.persist.validation;
+
+import hirs.attestationca.persist.entity.ArchivableEntity;
+import hirs.attestationca.persist.entity.userdefined.Certificate;
+import hirs.attestationca.persist.entity.userdefined.SupplyChainValidation;
+import hirs.attestationca.persist.entity.userdefined.certificate.CertificateAuthorityCredential;
+import hirs.attestationca.persist.entity.userdefined.certificate.EndorsementCredential;
+import hirs.attestationca.persist.entity.userdefined.certificate.PlatformCredential;
+import hirs.attestationca.persist.entity.userdefined.CertificateTest;
+import hirs.attestationca.persist.entity.userdefined.certificate.attributes.ComponentClass;
+import hirs.attestationca.persist.entity.userdefined.certificate.attributes.V2.AttributeStatus;
+import hirs.attestationca.persist.entity.userdefined.info.ComponentInfo;
+import hirs.attestationca.persist.entity.userdefined.info.HardwareInfo;
+import hirs.attestationca.persist.entity.userdefined.info.OSInfo;
+import hirs.attestationca.persist.entity.userdefined.info.NetworkInfo;
+import hirs.attestationca.persist.entity.userdefined.info.FirmwareInfo;
+import hirs.attestationca.persist.entity.userdefined.info.TPMInfo;
+import hirs.attestationca.persist.entity.userdefined.info.component.NICComponentInfo;
+import hirs.attestationca.persist.entity.userdefined.certificate.attributes.ComponentIdentifier;
+import hirs.attestationca.persist.entity.userdefined.certificate.attributes.V2.ComponentIdentifierV2;
+import hirs.attestationca.persist.entity.userdefined.report.DeviceInfoReport;
+import hirs.attestationca.persist.enums.AppraisalStatus;
+import hirs.utils.enums.DeviceInfoEnums;
+
+import org.apache.commons.io.IOUtils;
+import org.bouncycastle.asn1.ASN1Boolean;
+import org.bouncycastle.asn1.DERUTF8String;
+import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.cert.AttributeCertificateHolder;
+import org.bouncycastle.cert.AttributeCertificateIssuer;
+import org.bouncycastle.cert.X509AttributeCertificateHolder;
+import org.bouncycastle.cert.X509CertificateHolder;
+import org.bouncycastle.cert.X509v2AttributeCertificateBuilder;
+import org.bouncycastle.cert.X509v3CertificateBuilder;
+import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
+import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.openssl.PEMParser;
+import org.bouncycastle.operator.ContentSigner;
+import org.bouncycastle.operator.OperatorCreationException;
+import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
+
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.fail;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.math.BigInteger;
+import java.net.InetAddress;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.net.UnknownHostException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.SecureRandom;
+import java.security.Security;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.X509EncodedKeySpec;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Objects;
+import java.util.Set;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.Map;
+import java.util.HashMap;
+
+/**
+ * Tests the SupplyChainValidator class.
+ */
+public class SupplyChainCredentialValidatorTest {
+
+    private static final String JSON_FILE = "/config/component-class.json";
+    private static final String SAMPLE_PACCOR_OUTPUT_TXT = "/hirs/validation/sample_paccor_output.txt";
+    private static final String SAMPLE_PACCOR_OUTPUT_NOT_SPECIFIED_TXT
+            = "/hirs/validation/sample_paccor_output_not_specified_values.txt";
+    private static final String SAMPLE_TEST_PACCOR_CERT
+            = "/validation/platform_credentials_2/paccor_platform_cert.crt";
+
+    private static final String SAMPLE_PACCOR_OUTPUT_WITH_EXTRA_COMPONENT_TXT
+            = "/hirs/validation/sample_paccor_output_with_extra_component.txt";
+    private static HardwareInfo hardwareInfo;
+    private final SupplyChainCredentialValidator supplyChainCredentialValidator =
+            new SupplyChainCredentialValidator();
+
+    private final CredentialValidator credentialValidator =
+            new CredentialValidator();
+
+    private static KeyStore keyStore;
+    private static KeyStore emptyKeyStore;
+    /**
+     * File name used to initialize a test KeyStore.
+     */
+    static final String KEY_STORE_FILE_NAME = "TestKeyStore";
+    /**
+     * SecureRandom instance.
+     */
+    static final SecureRandom SECURE_RANDOM = new SecureRandom();
+
+    private static final String TEST_SIGNING_KEY = "/validation/platform_credentials/ca.pub";
+
+    private static final String TEST_PLATFORM_CRED =
+            "/validation/platform_credentials/plat_cert1.pem";
+    private static final String TEST_PLATFORM_CRED2 =
+            "/validation/platform_credentials/pciids_plat_cert_2-0.pem";
+
+    private static final String TEST_PLATFORM_CRED_BASE_CHASIS_COMBO =
+            "/validation/platform_credentials/Intel_pc5.pem";
+
+    private static final String TEST_BOARD_SERIAL_NUMBER = "GETY421001GV";
+    private static final String TEST_CHASSIS_SERIAL_NUMBER = "G6YK42300C87";
+    private static final String TEST_EK_CERT = "/certificates/nuc-2/tpmcert.pem";
+    private static final String TEST_EK_CERT_2 = "/certificates/nuc-1/tpmcert.pem";
+    private static final String TEST_COMPONENT_MANUFACTURER = "Intel";
+    private static final String TEST_COMPONENT_MODEL = "platform2018";
+    private static final String TEST_COMPONENT_REVISION = "1.0";
+    private static final String BAD_SERIAL = "BAD_SERIAL";
+
+    //-------Actual ST Micro Endorsement Credential Certificate Chain!--------------
+    private static final String EK_CERT = "";
+    private static final String INT_CA_CERT02 = "/certificates/fakestmtpmekint02.pem";
+
+    //-------Generated Intel Credential Certificate Chain--------------
+    private static final String INTEL_PLATFORM_CERT =
+            "/validation/platform_credentials/plat_cert3.pem";
+    private static final String INTEL_PLATFORM_CERT_2 =
+            "/validation/platform_credentials/Intel_pc2.pem";
+
+    private static final String INTEL_PLATFORM_CERT_3 =
+            "/validation/platform_credentials/pciids_plat_cert_2-0.pem";
+
+    private static final String INTEL_INT_CA =
+            "/validation/platform_credentials/intel_chain/root/intermediate1.crt";
+    private static final String FAKE_ROOT_CA =
+            "/validation/platform_credentials/intel_chain/root/rootca.crt";
+    private static final String PLATFORM_MANUFACTURER = "Intel";
+    private static final String PLATFORM_MODEL = "S2600KP";
+    private static final String PLATFORM_VERSION = "H76962-350";
+
+    //-------Original Intel Credential Certificate Chain--------------
+    private static final String INTEL_PLATFORM_CERT_ORIG =
+            "/certificates/fakeIntel_S2600KP_F00F00F00F00.pem";
+    private static final String INTEL_ORIG_INT_CA_ORIG =
+            "/certificates/fakeIntelIntermediateCA.pem";
+    private static final String FAKE_ROOT_CA_ORIG =
+            "/certificates/fakeCA.pem";
+
+    //-------Fake SGI Credential Certificate Chain--------------
+    private static final String SGI_PLATFORM_CERT = "/certificates/fakeSGI_J2_F00F00F0.pem";
+    private static final String SGI_INT_CA = "/certificates/fakeSGIIntermediateCA.pem";
+    private static final String SGI_CRED_SERIAL_NUMBER = "F00F00F0";
+
+    //-------Actual Intel NUC Platform --------------
+    private static final String NUC_PLATFORM_CERT =
+            "/certificates/Intel_nuc_pc.pem";
+    private static final String NUC_PLATFORM_CERT_SERIAL_NUMBER = "GETY421001DY";
+
+    private static final String NUC_PLATFORM_CERT2 =
+            "/certificates/Intel_nuc_pc2.pem";
+    private static final String NUC_PLATFORM_CERT_SERIAL_NUMBER2 = "GETY4210001M";
+
+    private static final String INTEL_SIGNING_KEY = "/certificates/IntelSigningKey_20April2017.pem";
+
+    private static final String NEW_NUC1 =
+            "/validation/platform_credentials/Intel_pc3.cer";
+
+    /**
+     * Sets up a KeyStore for testing.
+     *
+     * @throws KeyStoreException
+     *             if no Provider supports a KeyStoreSpi implementation for the specified type.
+     * @throws NoSuchAlgorithmException
+     *             if the algorithm used to check the integrity of the keystore cannot be found
+     * @throws CertificateException
+     *             if any of the certificates in the keystore could not be loaded
+     * @throws IOException
+     *             if there is an I/O or format problem with the keystore data, if a password is
+     *             required but not given, or if the given password was incorrect
+     */
+    @BeforeAll
+    public static void setUp() throws KeyStoreException, NoSuchAlgorithmException,
+            CertificateException, IOException {
+        Security.addProvider(new BouncyCastleProvider());
+
+        keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
+        emptyKeyStore = KeyStore.getInstance(KeyStore.getDefaultType());
+
+        char[] password = "password".toCharArray();
+        keyStore.load(null, password);
+
+        try (FileOutputStream fos = new FileOutputStream(KEY_STORE_FILE_NAME)) {
+            keyStore.store(fos, password);
+        }
+    }
+
+    /**
+     * Ensures the key store file is deleted after testing.
+     */
+    @AfterAll
+    public static void tearDown() {
+        File f = new File(KEY_STORE_FILE_NAME);
+        if (!f.delete()) {
+            fail("file was not cleaned up");
+        }
+
+    }
+
+    /**
+     * Checks if the ST Micro Endorsement Credential can be validated against the
+     * ST/GlobalSIgn Certificate Chain.
+     * @throws IOException if error occurs while reading files
+     * @throws URISyntaxException if error occurs while reading files
+     * @throws CertificateException if error occurs while processing X509 Certs
+     * @throws KeyStoreException if error occurs while processing Keystore
+     */
+    @Test
+    public final void testValidateEndorsementCredential()
+            throws URISyntaxException, IOException, CertificateException, KeyStoreException {
+        Certificate rootcacert, intermediateca02cert;
+
+        EndorsementCredential ekcert = new EndorsementCredential(
+                Files.readAllBytes(Paths.get(Objects.requireNonNull(getClass().getResource(TEST_EK_CERT)).toURI()))
+        );
+
+        intermediateca02cert = new CertificateAuthorityCredential(
+                Files.readAllBytes(Paths.get(Objects.requireNonNull(getClass().getResource(INT_CA_CERT02)).toURI()))
+        );
+
+        rootcacert = new CertificateAuthorityCredential(
+                Files.readAllBytes(Paths.get(Objects.requireNonNull(getClass().getResource(FAKE_ROOT_CA_ORIG)).toURI()))
+        );
+
+        try {
+            keyStore.setCertificateEntry("CA cert", rootcacert.getX509Certificate());
+            keyStore.setCertificateEntry("Intel Intermediate Cert",
+                    intermediateca02cert.getX509Certificate());
+
+            AppraisalStatus result = CredentialValidator.validateEndorsementCredential(
+                    ekcert, keyStore, true);
+            assertEquals(AppraisalStatus.Status.PASS, result.getAppStatus());
+            assertEquals(SupplyChainCredentialValidator.ENDORSEMENT_VALID, result.getMessage());
+        }  finally {
+            keyStore.deleteEntry("Intel Intermediate Cert");
+            keyStore.deleteEntry("CA cert");
+        }
+    }
+
+    /**
+     * Validates a generated cert chain pretending to be from Intel. Credential was generated
+     * with an intermediate CA. This tests the entire chain of validation back to the root CA.
+     *
+     * @throws IOException if error occurs while reading files
+     * @throws KeyStoreException if there's an issue string certs to the keystore
+     * @throws CertificateException if error occurs while ingesting a certificate
+     * @throws URISyntaxException if a URI can't be processed
+     */
+    @Test
+    public final void validateIntelPlatformCredentials()
+            throws URISyntaxException, IOException, CertificateException, KeyStoreException {
+        Certificate rootcacert, intermediatecacert;
+
+        intermediatecacert = new CertificateAuthorityCredential(
+                Files.readAllBytes(Paths.get(Objects.requireNonNull(getClass().getResource(INTEL_INT_CA)).toURI()))
+        );
+
+        rootcacert = new CertificateAuthorityCredential(
+                Files.readAllBytes(Paths.get(Objects.requireNonNull(getClass().getResource(FAKE_ROOT_CA)).toURI()))
+        );
+
+        try {
+            keyStore.setCertificateEntry("CA cert", rootcacert.getX509Certificate());
+            keyStore.setCertificateEntry("Intel Intermediate Cert",
+                    intermediatecacert.getX509Certificate());
+
+            byte[] certBytes = Files.readAllBytes(Paths.get(Objects.requireNonNull(CertificateTest.class.
+                    getResource(INTEL_PLATFORM_CERT)).toURI()));
+
+            PlatformCredential pc = new PlatformCredential(certBytes);
+
+            // The test certificate has expired. Test will accept expired certs.
+            AppraisalStatus result = CredentialValidator.validatePlatformCredential(
+                    pc, keyStore, true);
+
+            assertEquals(AppraisalStatus.Status.PASS, result.getAppStatus());
+            assertEquals(SupplyChainCredentialValidator.PLATFORM_VALID, result.getMessage());
+        } finally {
+            keyStore.deleteEntry("Intel Intermediate Cert");
+            keyStore.deleteEntry("CA cert");
+        }
+    }
+
+    /**
+     * Checks if the generated Intel Platform Credential can be validated with its attributes.
+     *
+     * @throws Exception If there are errors.
+     */
+    @Test
+    public final void validateIntelPlatformCredentialAttributes()
+            throws Exception {
+
+        byte[] certBytes = Files.readAllBytes(Paths.get(Objects.requireNonNull(CertificateTest.class.
+                getResource(INTEL_PLATFORM_CERT_2)).toURI()));
+
+        PlatformCredential pc = new PlatformCredential(certBytes);
+
+        DeviceInfoReport deviceInfoReport = buildReport(
+                new HardwareInfo(PLATFORM_MANUFACTURER, PLATFORM_MODEL,
+                        PLATFORM_VERSION, TEST_BOARD_SERIAL_NUMBER,
+                        TEST_CHASSIS_SERIAL_NUMBER, TEST_BOARD_SERIAL_NUMBER));
+
+        EndorsementCredential ec = new EndorsementCredential(
+                Files.readAllBytes(Paths.get(Objects.requireNonNull(getClass().getResource(TEST_EK_CERT)).toURI())));
+
+        AppraisalStatus result =
+                CredentialValidator.validatePlatformCredentialAttributes(pc,
+                        deviceInfoReport, ec);
+        assertEquals(AppraisalStatus.Status.PASS, result.getAppStatus());
+        assertEquals(SupplyChainCredentialValidator.PLATFORM_ATTRIBUTES_VALID,
+                result.getMessage());
+    }
+
+    /**
+     * Checks if the Platform Credential contains the serial number from
+     * the device in the platform serial number field.
+     * @throws Exception If there are errors.
+     *
+     * */
+    @Test
+    public final void validatePlatformCredentialWithDeviceBaseboard()
+            throws Exception {
+        DeviceInfoReport deviceInfoReport = buildReport(new HardwareInfo(
+                DeviceInfoEnums.NOT_SPECIFIED, DeviceInfoEnums.NOT_SPECIFIED,
+                DeviceInfoEnums.NOT_SPECIFIED, DeviceInfoEnums.NOT_SPECIFIED,
+                DeviceInfoEnums.NOT_SPECIFIED, TEST_BOARD_SERIAL_NUMBER));
+
+        byte[] certBytes = Files.readAllBytes(Paths.get(Objects.requireNonNull(CertificateTest.class.
+                getResource(INTEL_PLATFORM_CERT_2)).toURI()));
+
+        PlatformCredential pc = new PlatformCredential(certBytes);
+
+        EndorsementCredential ec = new EndorsementCredential(
+                Files.readAllBytes(Paths.get(Objects.requireNonNull(getClass().getResource(TEST_EK_CERT)).toURI())));
+
+        AppraisalStatus result =
+                CredentialValidator.validatePlatformCredentialAttributes(pc,
+                        deviceInfoReport, ec);
+        assertEquals(AppraisalStatus.Status.PASS, result.getAppStatus());
+        assertEquals(SupplyChainCredentialValidator.PLATFORM_ATTRIBUTES_VALID,
+                result.getMessage());
+    }
+
+    /**
+     * Checks if the Platform Credential contains the serial number from
+     * the device in the chassis serial number field.
+     */
+    @Test
+    public final void validatePlatformCredentialWithDeviceChassis()
+            throws Exception {
+
+        DeviceInfoReport deviceInfoReport = buildReport(new HardwareInfo(
+                DeviceInfoEnums.NOT_SPECIFIED, DeviceInfoEnums.NOT_SPECIFIED,
+                DeviceInfoEnums.NOT_SPECIFIED, DeviceInfoEnums.NOT_SPECIFIED,
+                TEST_CHASSIS_SERIAL_NUMBER, DeviceInfoEnums.NOT_SPECIFIED));
+
+        byte[] certBytes = Files.readAllBytes(Paths.get(Objects.requireNonNull(CertificateTest.class.
+                getResource(INTEL_PLATFORM_CERT_2)).toURI()));
+
+        PlatformCredential pc = new PlatformCredential(certBytes);
+
+        EndorsementCredential ec = new EndorsementCredential(
+                Files.readAllBytes(Paths.get(Objects.requireNonNull(getClass().getResource(TEST_EK_CERT)).toURI())));
+
+        AppraisalStatus result =
+                CredentialValidator.validatePlatformCredentialAttributes(pc,
+                        deviceInfoReport, ec);
+        assertEquals(AppraisalStatus.Status.PASS, result.getAppStatus());
+        assertEquals(SupplyChainCredentialValidator.PLATFORM_ATTRIBUTES_VALID,
+                result.getMessage());
+    }
+
+
+    /**
+     * Checks if the NUC Platform Credential contains the serial number from
+     * the device as a baseboard component in the serial number field.
+     * @throws Exception If there are errors.
+     */
+    @Test
+    public final void validatePlatformCredentialWithDeviceSystemSerialNumber()
+            throws Exception {
+
+        DeviceInfoReport deviceInfoReport = buildReport(new HardwareInfo(
+                DeviceInfoEnums.NOT_SPECIFIED, DeviceInfoEnums.NOT_SPECIFIED,
+                DeviceInfoEnums.NOT_SPECIFIED, TEST_BOARD_SERIAL_NUMBER,
+                DeviceInfoEnums.NOT_SPECIFIED, DeviceInfoEnums.NOT_SPECIFIED));
+
+        byte[] certBytes = Files.readAllBytes(Paths.get(Objects.requireNonNull(CertificateTest.class.
+                getResource(INTEL_PLATFORM_CERT_2)).toURI()));
+
+        PlatformCredential pc = new PlatformCredential(certBytes);
+
+        EndorsementCredential ec = new EndorsementCredential(
+                Files.readAllBytes(Paths.get(Objects.requireNonNull(getClass().getResource(TEST_EK_CERT)).toURI())));
+
+        AppraisalStatus result =
+                CredentialValidator.validatePlatformCredentialAttributes(pc,
+                        deviceInfoReport, ec);
+        assertEquals(AppraisalStatus.Status.PASS, result.getAppStatus());
+        assertEquals(SupplyChainCredentialValidator.PLATFORM_ATTRIBUTES_VALID,
+                result.getMessage());
+    }
+
+    /**
+     * Checks if the Platform Credential validator appropriately fails
+     * when there are no serial numbers returned from the device.
+     * @throws Exception If there are errors.
+     */
+    @Test
+    public final void validatePlatformCredentialWithNoDeviceSerialNumbers()
+            throws Exception {
+
+        DeviceInfoReport deviceInfoReport = buildReport(
+                new HardwareInfo(PLATFORM_MANUFACTURER, PLATFORM_MODEL,
+                        PLATFORM_VERSION, DeviceInfoEnums.NOT_SPECIFIED,
+                        DeviceInfoEnums.NOT_SPECIFIED, DeviceInfoEnums.NOT_SPECIFIED));
+
+        byte[] certBytes = Files.readAllBytes(Paths.get(Objects.requireNonNull(CertificateTest.class.
+                getResource(INTEL_PLATFORM_CERT_2)).toURI()));
+
+        PlatformCredential pc = new PlatformCredential(certBytes);
+
+        EndorsementCredential ec = new EndorsementCredential(
+                Files.readAllBytes(Paths.get(Objects.requireNonNull(getClass().getResource(TEST_EK_CERT)).toURI())));
+
+        String expectedMessage = "Platform serial did not match device info";
+
+        AppraisalStatus result =
+                CredentialValidator.validatePlatformCredentialAttributes(
+                        pc, deviceInfoReport, ec);
+        assertEquals(AppraisalStatus.Status.FAIL, result.getAppStatus());
+        assertEquals(expectedMessage, result.getMessage());
+    }
+
+    /**
+     * Checks if the Platform Credential validator appropriately fails
+     * when there are no serial numbers matching any of the platform info from the device.
+     * @throws Exception If there are errors.
+     */
+    @Test
+    public final void validatePlatformCredentialCombinedWithNoMatchedDeviceSerialNumbers()
+            throws Exception {
+
+        DeviceInfoReport deviceInfoReport = buildReport(
+                new HardwareInfo(DeviceInfoEnums.NOT_SPECIFIED, DeviceInfoEnums.NOT_SPECIFIED,
+                        DeviceInfoEnums.NOT_SPECIFIED, "zzz", "aaa", "bbb"));
+
+        byte[] certBytes = Files.readAllBytes(Paths.get(Objects.requireNonNull(CertificateTest.class.
+                getResource(INTEL_PLATFORM_CERT_2)).toURI()));
+
+        PlatformCredential pc = new PlatformCredential(certBytes);
+
+        EndorsementCredential ec = new EndorsementCredential(
+                Files.readAllBytes(Paths.get(Objects.requireNonNull(getClass().getResource(TEST_EK_CERT)).toURI())));
+
+        String expectedMessage = "Platform serial did not match device info";
+
+        AppraisalStatus result =
+                CredentialValidator.validatePlatformCredentialAttributes(
+                        pc, deviceInfoReport, ec);
+        assertEquals(AppraisalStatus.Status.FAIL, result.getAppStatus());
+        assertEquals(expectedMessage, result.getMessage());
+    }
+
+    /**
+     * Checks if a cert can be validated against the given public key.
+     *
+     * @throws IOException if error occurs while reading files
+     * @throws InvalidKeySpecException if error occurs while generating the PublicKey
+     * @throws NoSuchAlgorithmException if error occurs while getting RSA KeyFactory
+     * @throws URISyntaxException if error occurs constructing test cert path
+     * @throws SupplyChainValidatorException if error occurs due to using null certificates
+     */
+    @Test
+    public final void validateTestCertificateAgainstPublicKey()
+            throws IOException, InvalidKeySpecException, NoSuchAlgorithmException,
+            URISyntaxException, SupplyChainValidatorException {
+        PlatformCredential credential = new PlatformCredential(Paths.get(
+                Objects.requireNonNull(this.getClass().getResource(TEST_PLATFORM_CRED)).toURI()
+        ));
+
+        InputStream stream = this.getClass().getResourceAsStream(TEST_SIGNING_KEY);
+        assert stream != null;
+        PEMParser pemParser =
+                new PEMParser(new BufferedReader(new InputStreamReader(stream, StandardCharsets.UTF_8)));
+        SubjectPublicKeyInfo info = (SubjectPublicKeyInfo) pemParser.readObject();
+        pemParser.close();
+        PublicKey signingKey = KeyFactory.getInstance("RSA")
+                .generatePublic(new X509EncodedKeySpec(info.getEncoded()));
+
+        assertTrue(SupplyChainCredentialValidator.signatureMatchesPublicKey(
+                credential.getX509AttributeCertificateHolder(), signingKey)
+        );
+    }
+
+    /**
+     * Negative test to check if validation against a public key can fail. Generates a random
+     * key pair and attempts to validate it against the Intel cert, which is expected to fail.
+     *
+     * @throws IOException if error occurs while reading files
+     * @throws URISyntaxException if an error occurs while constructing test resource's URI
+     * @throws SupplyChainValidatorException if error occurs due to using null certificates
+     */
+    @Test
+    public final void checkTestCertificateAgainstInvalidPublicKey()
+            throws IOException, URISyntaxException, SupplyChainValidatorException {
+        PlatformCredential credential = new PlatformCredential(
+                Paths.get(Objects.requireNonNull(this.getClass().getResource(TEST_PLATFORM_CRED)).toURI())
+        );
+
+        PublicKey invalidPublicKey = createKeyPair().getPublic();
+
+        assertFalse(SupplyChainCredentialValidator.signatureMatchesPublicKey(
+                credential.getX509AttributeCertificateHolder(), invalidPublicKey)
+        );
+    }
+
+    /**
+     * Creates a self-signed "CA" cert, intermediate cert signed by the "CA", and a sample "client"
+     * cert signed by the intermediate cert, and an attribute cert using the "client" cert. Attempts
+     * to validate the attribute cert against a Set including the "CA" cert and the intermediate
+     * cert. Validation should pass because the entire chain is included in the Set.
+     *
+     * @throws SupplyChainValidatorException if error occurs due to using null certificates
+     */
+    @Test
+    public final void verifyX509AttributeCertificateAgainstIntermediate()
+            throws SupplyChainValidatorException {
+        KeyPair caKeyPair = createKeyPair();
+        KeyPair intermediateKeyPair = createKeyPair();
+        KeyPair targetKeyPair = createKeyPair();
+        Set<X509Certificate> trustedCerts = new HashSet<X509Certificate>();
+
+        X509Certificate caCert = createSelfSignedCertificate(caKeyPair);
+        X509Certificate intermediateCert =
+                createCertSignedByAnotherCert(intermediateKeyPair, caKeyPair.getPrivate(), caCert);
+        X509Certificate targetCert =
+                createCertSignedByAnotherCert(targetKeyPair, intermediateKeyPair.getPrivate(),
+                        intermediateCert);
+        X509AttributeCertificateHolder attrCert =
+                createAttributeCert(targetCert, intermediateCert,
+                        intermediateKeyPair.getPrivate());
+
+        trustedCerts.add(caCert);
+        trustedCerts.add(intermediateCert);
+        boolean assertion = SupplyChainCredentialValidator.validateCertChain(attrCert,
+                trustedCerts).isEmpty();
+
+        assertTrue(assertion);
+
+        try {
+            keyStore.setCertificateEntry("CA cert", caCert);
+            keyStore.setCertificateEntry("Intermediate Cert", intermediateCert);
+            assertion = SupplyChainCredentialValidator.verifyCertificate(attrCert,
+                    keyStore).isEmpty();
+            assertTrue(assertion);
+        } catch (Exception e) {
+            fail("Unexpected error occurred while verifying certificate", e);
+        }
+    }
+
+    /**
+     * Creates a self-signed "CA" cert, intermediate cert signed by the "CA", and a sample "client"
+     * cert signed by the intermediate cert, and an attribute cert based on the "client" cert.
+     * Attempts to validate the attribute cert only against the self-signed "CA" cert, which fails
+     * as expected.
+     *
+     * @throws SupplyChainValidatorException if error occurs due to using null certificates
+     */
+    @Test
+    public final void verifyX509AttributeCertificateFailsIfSigningCertNotInList()
+            throws SupplyChainValidatorException {
+        KeyPair caKeyPair = createKeyPair();
+        KeyPair intermediateKeyPair = createKeyPair();
+        KeyPair targetKeyPair = createKeyPair();
+        Set<X509Certificate> trustedCerts = new HashSet<X509Certificate>();
+
+        X509Certificate caCert = createSelfSignedCertificate(caKeyPair);
+        X509Certificate intermediateCert =
+                createCertSignedByAnotherCert(intermediateKeyPair, caKeyPair.getPrivate(), caCert);
+        X509Certificate targetCert =
+                createCertSignedByAnotherCert(targetKeyPair, intermediateKeyPair.getPrivate(),
+                        intermediateCert);
+        X509AttributeCertificateHolder attrCert =
+                createAttributeCert(targetCert, intermediateCert,
+                        intermediateKeyPair.getPrivate());
+
+        trustedCerts.add(caCert);
+
+        boolean assertion = SupplyChainCredentialValidator.validateCertChain(attrCert,
+                trustedCerts).isEmpty();
+        assertFalse(assertion);
+
+        try {
+            keyStore.setCertificateEntry("CA cert", caCert);
+
+            assertion = SupplyChainCredentialValidator.verifyCertificate(attrCert,
+                    keyStore).isEmpty();
+            assertFalse(assertion);
+        } catch (Exception e) {
+            fail("Unexpected error occurred while verifying certificate", e);
+        }
+    }
+
+    /**
+     * Tests that an X509AttributeCertificate signed by a self-signed cert will pass validation.
+     *
+     * @throws SupplyChainValidatorException if error occurs due to using null certificates
+     */
+    @Test
+    public final void verifyX509AttributeCertificateAgainstCA()
+            throws SupplyChainValidatorException {
+        KeyPair caKeyPair = createKeyPair();
+        KeyPair targetKeyPair = createKeyPair();
+        Set<X509Certificate> trustedCerts = new HashSet<X509Certificate>();
+
+        X509Certificate caCert = createSelfSignedCertificate(caKeyPair);
+        X509Certificate targetCert =
+                createCertSignedByAnotherCert(targetKeyPair, caKeyPair.getPrivate(), caCert);
+        X509AttributeCertificateHolder attrCert =
+                createAttributeCert(targetCert, caCert, caKeyPair.getPrivate());
+
+        trustedCerts.add(caCert);
+
+        boolean assertion = SupplyChainCredentialValidator.validateCertChain(
+                attrCert, trustedCerts).isEmpty();
+        assertTrue(assertion);
+
+        try {
+            keyStore.setCertificateEntry("CA cert", caCert);
+
+            assertion = SupplyChainCredentialValidator.verifyCertificate(
+                    attrCert, keyStore).isEmpty();
+            assertTrue(assertion);
+        } catch (Exception e) {
+            fail("Unexpected error occurred while verifying certificate", e);
+        }
+    }
+
+    /**
+     * Creates a self-signed "CA" cert, intermediate cert signed by the "CA", and a sample "client"
+     * cert signed by the intermediate cert. Attempts to validate the cert only against a Set
+     * including the "CA" cert and the intermediate cert. Validation should pass because the entire
+     * chain is included in the Set.
+     *
+     * @throws SupplyChainValidatorException if error occurs due to using null certificates
+     */
+    @Test
+    public final void verifyX509CertificateAgainstIntermediate()
+            throws SupplyChainValidatorException {
+        KeyPair caKeyPair = createKeyPair();
+        KeyPair intermediateKeyPair = createKeyPair();
+        KeyPair targetKeyPair = createKeyPair();
+        Set<X509Certificate> trustedCerts = new HashSet<X509Certificate>();
+
+        X509Certificate caCert = createSelfSignedCertificate(caKeyPair);
+        X509Certificate intermediateCert =
+                createCertSignedByAnotherCert(intermediateKeyPair, caKeyPair.getPrivate(), caCert);
+        X509Certificate targetCert =
+                createCertSignedByAnotherCert(targetKeyPair, intermediateKeyPair.getPrivate(),
+                        intermediateCert);
+
+        trustedCerts.add(caCert);
+        trustedCerts.add(intermediateCert);
+
+        boolean assertion = SupplyChainCredentialValidator.validateCertChain(targetCert,
+                trustedCerts).isEmpty();
+        assertTrue(assertion);
+
+        try {
+            keyStore.setCertificateEntry("CA cert", caCert);
+            keyStore.setCertificateEntry("Intermediate Cert", intermediateCert);
+
+            assertTrue(SupplyChainCredentialValidator.verifyCertificate(targetCert,
+                    keyStore));
+        } catch (Exception e) {
+            fail("Unexpected error occurred while verifying certificate", e);
+        }
+    }
+
+    /**
+     * Creates a self-signed "CA" cert, intermediate cert signed by the "CA", and a sample client
+     * cert signed by the intermediate cert. Attempts to validate the cert only against the
+     * self-signed cert, which fails as expected.
+     *
+     * @throws SupplyChainValidatorException if error occurs due to using null certificates
+     */
+    @Test
+    public final void verifyX509CertificateFailsIfSigningCertNotInList()
+            throws SupplyChainValidatorException {
+        KeyPair caKeyPair = createKeyPair();
+        KeyPair intermediateKeyPair = createKeyPair();
+        KeyPair targetKeyPair = createKeyPair();
+        Set<X509Certificate> trustedCerts = new HashSet<X509Certificate>();
+
+        X509Certificate caCert = createSelfSignedCertificate(caKeyPair);
+        X509Certificate intermediateCert =
+                createCertSignedByAnotherCert(intermediateKeyPair, caKeyPair.getPrivate(), caCert);
+        X509Certificate targetCert =
+                createCertSignedByAnotherCert(targetKeyPair, intermediateKeyPair.getPrivate(),
+                        intermediateCert);
+
+        trustedCerts.add(caCert);
+
+        boolean assertion = SupplyChainCredentialValidator.validateCertChain(targetCert,
+                trustedCerts).isEmpty();
+        assertFalse(assertion);
+
+        try {
+            keyStore.setCertificateEntry("CA cert", caCert);
+
+            assertFalse(SupplyChainCredentialValidator.verifyCertificate(targetCert,
+                    keyStore));
+        } catch (Exception e) {
+            fail("Unexpected error occurred while verifying certificate", e);
+        }
+    }
+
+    /**
+     * Tests that an X509Certificate signed by a self-signed cert will pass validation.
+     *
+     * @throws SupplyChainValidatorException if error occurs due to using null certificates
+     */
+    @Test
+    public final void verifyX509CertificateAgainstCA() throws SupplyChainValidatorException {
+        KeyPair caKeyPair = createKeyPair();
+        KeyPair targetKeyPair = createKeyPair();
+        Set<X509Certificate> trustedCerts = new HashSet<X509Certificate>();
+
+        X509Certificate caCert = createSelfSignedCertificate(caKeyPair);
+        X509Certificate targetCert =
+                createCertSignedByAnotherCert(targetKeyPair, caKeyPair.getPrivate(), caCert);
+
+        trustedCerts.add(caCert);
+
+        boolean assertion = SupplyChainCredentialValidator.validateCertChain(targetCert,
+                trustedCerts).isEmpty();
+        assertTrue(assertion);
+
+        try {
+            keyStore.setCertificateEntry("CA cert", caCert);
+
+            assertTrue(SupplyChainCredentialValidator.verifyCertificate(targetCert,
+                    keyStore));
+        } catch (Exception e) {
+            fail("Unexpected error occurred while verifying certificate", e);
+        }
+    }
+
+    /**
+     * Verifies that when the test device's serial number matches the platform credential's
+     * board serial number, and the credential can be validated with the keystore,
+     * validation passes. This should result in an error as keystores should never
+     * be empty.
+     *
+     * @throws IOException an error occurs when parsing the certificate
+     * @throws URISyntaxException an error occurs parsing the certificate file path
+     */
+    @Test
+    public final void verifyPlatformCredentialWithBadKeyStore()
+            throws URISyntaxException, IOException {
+        byte[] certBytes = Files.readAllBytes(Paths.get(Objects.requireNonNull(CertificateTest.class.getResource(
+                INTEL_PLATFORM_CERT)).toURI()));
+
+        PlatformCredential pc = new PlatformCredential(certBytes);
+
+        String expectedMessage = "Can't validate platform credential without an "
+                + "initialized trust store";
+
+        AppraisalStatus result = CredentialValidator.validatePlatformCredential(
+                pc, emptyKeyStore, true);
+        assertEquals(AppraisalStatus.Status.FAIL, result.getAppStatus());
+        assertEquals(expectedMessage, result.getMessage());
+    }
+
+    /**
+     * Verifies that a null check is performed on the platform credential path
+     * when validating platform credentials.
+     */
+    @Test
+    public final void verifyPlatformCredentialNullCredentialPath() {
+        String expectedMessage = "Can't validate platform credential without "
+                + "a platform credential";
+
+        AppraisalStatus result = CredentialValidator.validatePlatformCredential(
+                null, keyStore, true);
+        assertEquals(AppraisalStatus.Status.FAIL, result.getAppStatus());
+        assertEquals(expectedMessage, result.getMessage());
+    }
+
+    /**
+     * Verifies that a null check is performed on the keyStore
+     * when validating platform credentials.
+     *
+     * @throws IOException an error occurs when parsing the certificate
+     * @throws URISyntaxException an error occurs parsing the certificate file path
+     */
+    @Test
+    public final void verifyPlatformCredentialNullKeyStore()
+            throws URISyntaxException, IOException {
+        byte[] certBytes = Files.readAllBytes(Paths.get(Objects.requireNonNull(CertificateTest.class.getResource(
+                INTEL_PLATFORM_CERT)).toURI()));
+
+        PlatformCredential pc = new PlatformCredential(certBytes);
+
+        String expectedMessage = "Can't validate platform credential without an "
+                + "Issuer Cert in the Trust Store";
+
+        AppraisalStatus result = CredentialValidator.validatePlatformCredential(pc, null,
+                true);
+        assertEquals(AppraisalStatus.Status.FAIL, result.getAppStatus());
+        assertEquals(expectedMessage, result.getMessage());
+    }
+
+    /**
+     * Verifies that a null check is performed on the device info report
+     * when validating platform credentials.
+     *
+     * @throws IOException an error occurs when parsing the certificate
+     * @throws URISyntaxException an error occurs parsing the certificate file path
+     */
+    @Test
+    public final void verifyPlatformCredentialNullDeviceInfoReport()
+            throws URISyntaxException, IOException {
+        byte[] certBytes = Files.readAllBytes(Paths.get(Objects.requireNonNull(CertificateTest.class.getResource(
+                INTEL_PLATFORM_CERT_2)).toURI()));
+
+        PlatformCredential pc = new PlatformCredential(certBytes);
+
+        EndorsementCredential ec = new EndorsementCredential(
+                Files.readAllBytes(Paths.get(Objects.requireNonNull(getClass().getResource(TEST_EK_CERT)).toURI())));
+
+        String expectedMessage = "Can't validate platform credential attributes without a "
+                + "device info report";
+
+        AppraisalStatus result =
+                CredentialValidator.validatePlatformCredentialAttributes(pc, null, ec);
+        assertEquals(AppraisalStatus.Status.FAIL, result.getAppStatus());
+        assertEquals(expectedMessage, result.getMessage());
+    }
+
+    /**
+     * Tests that issuer/subject distinguished names can be properly verified as equal even
+     * if their elements are in different orders.
+     * @throws URISyntaxException failed to read certificate
+     * @throws IOException failed to read certificate
+     * @throws KeyStoreException failed to read key store
+     * @throws SupplyChainValidatorException missing credential
+     */
+
+    @Test
+    public final void testPlatformDnEquals() throws URISyntaxException, IOException,
+            KeyStoreException, SupplyChainValidatorException {
+        Certificate signingCert;
+        signingCert = new CertificateAuthorityCredential(
+                Files.readAllBytes(Paths.get(Objects.requireNonNull(getClass().getResource(INTEL_SIGNING_KEY)).toURI()))
+        );
+
+        byte[] certBytes = Files.readAllBytes(Paths.get(Objects.requireNonNull(CertificateTest.class.
+                getResource(NEW_NUC1)).toURI()));
+
+        PlatformCredential pc = new PlatformCredential(certBytes);
+
+        X509AttributeCertificateHolder attributeCert = pc.getX509AttributeCertificateHolder();
+
+        X509Certificate caX509 = signingCert.getX509Certificate();
+
+        assertTrue(SupplyChainCredentialValidator.issuerMatchesSubjectDN(
+                attributeCert, caX509));
+    }
+
+    /**
+     * Tests that issuer/subject distinguished names can be properly verified as being unequal
+     * if their elements don't match.
+     * @throws URISyntaxException failed to read certificate
+     * @throws IOException failed to read certificate
+     * @throws KeyStoreException failed to read key store
+     * @throws SupplyChainValidatorException missing credential
+     */
+    @Test
+    public final void testPlatformDnNotEquals() throws URISyntaxException, IOException,
+            KeyStoreException, SupplyChainValidatorException {
+        Certificate signingCert;
+        signingCert = new CertificateAuthorityCredential(
+                Files.readAllBytes(Paths.get(Objects.requireNonNull(getClass().getResource(INTEL_INT_CA)).toURI()))
+        );
+
+        byte[] certBytes = Files.readAllBytes(Paths.get(Objects.requireNonNull(CertificateTest.class.
+                getResource(NEW_NUC1)).toURI()));
+
+        PlatformCredential pc = new PlatformCredential(certBytes);
+
+        X509AttributeCertificateHolder attributeCert = pc.getX509AttributeCertificateHolder();
+
+        X509Certificate caX509 = signingCert.getX509Certificate();
+
+        assertFalse(SupplyChainCredentialValidator.issuerMatchesSubjectDN(
+                attributeCert, caX509));
+    }
+
+    /**
+     * Tests that issuer/subject distinguished names can be properly verified as equal.
+     * @throws URISyntaxException failed to read certificate
+     * @throws IOException failed to read certificate
+     * @throws KeyStoreException failed to read key store
+     * @throws SupplyChainValidatorException missing credential
+     */
+    @Test
+    public final void testEndorsementDnEquals() throws URISyntaxException, IOException,
+            KeyStoreException, SupplyChainValidatorException {
+        Certificate signingCert;
+        signingCert = new CertificateAuthorityCredential(
+                Files.readAllBytes(Paths.get(Objects.requireNonNull(getClass().getResource(INT_CA_CERT02)).toURI()))
+        );
+
+        byte[] certBytes = Files.readAllBytes(Paths.get(Objects.requireNonNull(CertificateTest.class.
+                getResource(TEST_EK_CERT)).toURI()));
+
+        EndorsementCredential ec = new EndorsementCredential(certBytes);
+
+        X509Certificate x509Cert = ec.getX509Certificate();
+
+        X509Certificate caX509 = signingCert.getX509Certificate();
+
+        assertTrue(SupplyChainCredentialValidator.issuerMatchesSubjectDN(
+                x509Cert, caX509));
+    }
+
+    /**
+     * Tests that issuer/subject distinguished names can be properly verified as being unequal
+     * if their elements don't match.
+     * @throws URISyntaxException failed to read certificate
+     * @throws IOException failed to read certificate
+     * @throws KeyStoreException failed to read key store
+     * @throws SupplyChainValidatorException missing credential
+     */
+    @Test
+    public final void testEndorsementDnNotEquals() throws URISyntaxException, IOException,
+            KeyStoreException, SupplyChainValidatorException {
+        Certificate signingCert;
+        signingCert = new CertificateAuthorityCredential(
+                Files.readAllBytes(Paths.get(Objects.requireNonNull(getClass().getResource(INTEL_INT_CA)).toURI()))
+        );
+
+        byte[] certBytes = Files.readAllBytes(Paths.get(Objects.requireNonNull(CertificateTest.class.
+                getResource(TEST_EK_CERT)).toURI()));
+
+        EndorsementCredential ec = new EndorsementCredential(certBytes);
+
+        X509Certificate x509Cert = ec.getX509Certificate();
+
+        X509Certificate caX509 = signingCert.getX509Certificate();
+
+        assertFalse(SupplyChainCredentialValidator.issuerMatchesSubjectDN(
+                x509Cert, caX509));
+    }
+
+    private static DeviceInfoReport setupDeviceInfoReport() {
+        hardwareInfo = new HardwareInfo(
+                "ACME",
+                "anvil",
+                "3.0",
+                "1234",
+                "567",
+                "890");
+
+        DeviceInfoReport deviceInfoReport = mock(DeviceInfoReport.class);
+        when(deviceInfoReport.getHardwareInfo()).thenReturn(hardwareInfo);
+        return deviceInfoReport;
+    }
+
+    private static DeviceInfoReport setupDeviceInfoReportWithComponents() throws IOException {
+        return setupDeviceInfoReportWithComponents(SAMPLE_PACCOR_OUTPUT_TXT);
+    }
+
+    private static DeviceInfoReport setupDeviceInfoReportWithNotSpecifiedComponents()
+            throws IOException {
+        return setupDeviceInfoReportWithComponents(SAMPLE_PACCOR_OUTPUT_NOT_SPECIFIED_TXT);
+    }
+
+    private static DeviceInfoReport setupDeviceInfoReportWithComponents(
+            final String paccorOutputResource) throws IOException {
+        DeviceInfoReport deviceInfoReport = setupDeviceInfoReport();
+        URL url = SupplyChainCredentialValidator.class.getResource(paccorOutputResource);
+        String paccorOutputString = IOUtils.toString(url, StandardCharsets.UTF_8);
+        when(deviceInfoReport.getPaccorOutputString()).thenReturn(paccorOutputString);
+        return deviceInfoReport;
+    }
+
+    /**
+     * Tests that isMatch works correctly in comparing component info to component identifier.
+     */
+    @Test
+    public void testMatcher() {
+        NICComponentInfo nicComponentInfo = new NICComponentInfo("Intel Corporation",
+                "Ethernet Connection I217-V",
+                "23:94:17:ba:86:5e",
+                "00");
+
+        ComponentIdentifier pcComponentIdentifier = new ComponentIdentifier(
+                new DERUTF8String(nicComponentInfo.getComponentManufacturer()),
+                new DERUTF8String(nicComponentInfo.getComponentModel()),
+                new DERUTF8String(nicComponentInfo.getComponentSerial()),
+                new DERUTF8String(nicComponentInfo.getComponentRevision()),
+                null,
+                ASN1Boolean.TRUE,
+                Collections.emptyList()
+        );
+
+        assertTrue(
+                CertificateAttributeScvValidator.isMatch(null, pcComponentIdentifier,
+                        nicComponentInfo)
+        );
+
+        pcComponentIdentifier = new ComponentIdentifier(
+                new DERUTF8String(nicComponentInfo.getComponentManufacturer()),
+                new DERUTF8String(nicComponentInfo.getComponentModel()),
+                new DERUTF8String("ab:cd:ef:fe:dc:ba"),
+                new DERUTF8String(nicComponentInfo.getComponentRevision()),
+                null,
+                ASN1Boolean.TRUE,
+                Collections.emptyList()
+        );
+
+        assertFalse(
+                CertificateAttributeScvValidator.isMatch(null, pcComponentIdentifier,
+                        nicComponentInfo)
+        );
+    }
+
+    private PlatformCredential setupMatchingPlatformCredential(
+            final DeviceInfoReport deviceInfoReport) throws IOException {
+        PlatformCredential platformCredential = mock(PlatformCredential.class);
+
+        when(platformCredential.getCredentialType()).thenReturn(
+                PlatformCredential.CERTIFICATE_TYPE_2_0);
+        when(platformCredential.getManufacturer())
+                .thenReturn(hardwareInfo.getManufacturer());
+        when(platformCredential.getModel())
+                .thenReturn(hardwareInfo.getProductName());
+        when(platformCredential.getPlatformSerial())
+                .thenReturn(hardwareInfo.getBaseboardSerialNumber());
+        when(platformCredential.getVersion())
+                .thenReturn(hardwareInfo.getVersion());
+
+        List<ComponentInfo> deviceInfoComponents
+                = SupplyChainCredentialValidator.getComponentInfoFromPaccorOutput(
+                deviceInfoReport.getPaccorOutputString());
+        List<ComponentIdentifier> componentIdentifierList = new ArrayList<>();
+        for (ComponentInfo deviceInfoComponent : deviceInfoComponents) {
+            DERUTF8String serial = null;
+            DERUTF8String revision = null;
+            if (deviceInfoComponent.getComponentSerial() != null) {
+                serial = new DERUTF8String(deviceInfoComponent.getComponentSerial());
+            }
+            if (deviceInfoComponent.getComponentRevision() != null) {
+                revision = new DERUTF8String(deviceInfoComponent.getComponentRevision());
+            }
+            componentIdentifierList.add(new ComponentIdentifier(
+                    new DERUTF8String(deviceInfoComponent.getComponentManufacturer()),
+                    new DERUTF8String(deviceInfoComponent.getComponentModel()),
+                    serial,
+                    revision,
+                    null,
+                    ASN1Boolean.TRUE,
+                    Collections.emptyList()
+            ));
+
+        }
+
+        when(platformCredential.getComponentIdentifiers()).thenReturn(componentIdentifierList);
+
+        return platformCredential;
+    }
+
+    /**
+     * Tests that TPM 2.0 Platform Credentials validate correctly against the device info report
+     * when there are no components.
+     * @throws IOException if unable to set up DeviceInfoReport from resource file
+     */
+    @Test
+    public final void testValidatePlatformCredentialAttributesV2p0NoComponentsPass()
+            throws IOException {
+        DeviceInfoReport deviceInfoReport = setupDeviceInfoReport();
+        PlatformCredential platformCredential = setupMatchingPlatformCredential(deviceInfoReport);
+
+        AppraisalStatus appraisalStatus = CertificateAttributeScvValidator
+                .validatePlatformCredentialAttributesV2p0(platformCredential, deviceInfoReport);
+        assertEquals(AppraisalStatus.Status.PASS,
+                appraisalStatus.getAppStatus());
+        assertEquals(SupplyChainCredentialValidator.PLATFORM_ATTRIBUTES_VALID,
+                appraisalStatus.getMessage());
+    }
+
+    /**
+     * Tests that TPM 2.0 Platform Credentials validate correctly against the device info report
+     * when there are components present.
+     * @throws IOException if unable to set up DeviceInfoReport from resource file
+     */
+    @Test
+    public final void testValidatePlatformCredentialAttributesV2p0WithComponentsPass()
+            throws IOException {
+        DeviceInfoReport deviceInfoReport = setupDeviceInfoReportWithComponents();
+        PlatformCredential platformCredential = setupMatchingPlatformCredential(deviceInfoReport);
+
+        AppraisalStatus appraisalStatus = CertificateAttributeScvValidator
+                .validatePlatformCredentialAttributesV2p0(platformCredential, deviceInfoReport);
+        assertEquals(AppraisalStatus.Status.PASS, appraisalStatus.getAppStatus());
+        assertEquals(SupplyChainCredentialValidator.PLATFORM_ATTRIBUTES_VALID,
+                appraisalStatus.getMessage());
+    }
+
+    /**
+     * Tests that TPM 2.0 Platform Credentials validate correctly against the device info report
+     * when there are components present, and when the PlatformSerial field holds the system's
+     * serial number instead of the baseboard serial number.
+     * @throws IOException if unable to set up DeviceInfoReport from resource file
+     */
+    @Test
+    public final void testValPCAttributesV2p0WithComponentsPassPlatformSerialWithSystemSerial()
+            throws IOException {
+        DeviceInfoReport deviceInfoReport = setupDeviceInfoReportWithComponents();
+        PlatformCredential platformCredential = setupMatchingPlatformCredential(deviceInfoReport);
+        when(platformCredential.getPlatformSerial())
+                .thenReturn(hardwareInfo.getSystemSerialNumber());
+
+        AppraisalStatus appraisalStatus = CertificateAttributeScvValidator
+                .validatePlatformCredentialAttributesV2p0(platformCredential, deviceInfoReport);
+        assertEquals(AppraisalStatus.Status.PASS, appraisalStatus.getAppStatus());
+        assertEquals(SupplyChainCredentialValidator.PLATFORM_ATTRIBUTES_VALID,
+                appraisalStatus.getMessage());
+    }
+
+    /**
+     * Tests that TPM 2.0 Platform Credentials validate correctly against the device info report
+     * when there are components present, and when the PlatformSerial field holds the system's
+     * serial number instead of the baseboard serial number.
+     * @throws IOException if unable to set up DeviceInfoReport from resource file
+     * @throws URISyntaxException failed to read certificate
+     */
+    @Test
+    public final void testValPCAttributesV2p0WithComponentsPassPlatformSerialWithSystemSerial2()
+            throws IOException, URISyntaxException {
+        DeviceInfoReport deviceInfoReport = setupDeviceInfoReportWithNotSpecifiedComponents();
+        PlatformCredential platformCredential = new PlatformCredential(
+                Files.readAllBytes(Paths.get(Objects.requireNonNull(CertificateTest.class.
+                        getResource((SAMPLE_TEST_PACCOR_CERT))).toURI())));
+
+        AppraisalStatus appraisalStatus = CertificateAttributeScvValidator
+                .validatePlatformCredentialAttributesV2p0(platformCredential, deviceInfoReport);
+        assertEquals(AppraisalStatus.Status.FAIL, appraisalStatus.getAppStatus());
+    }
+
+    /**
+     * Tests that the SupplyChainCredentialValidator fails when required fields are null.
+     * @throws IOException if unable to set up DeviceInfoReport from resource file
+     */
+    @Test
+    public final void testValidatePlatformCredentialAttributesV2p0RequiredFieldsNull()
+            throws IOException {
+        DeviceInfoReport deviceInfoReport = setupDeviceInfoReportWithComponents();
+
+        PlatformCredential platformCredential = setupMatchingPlatformCredential(deviceInfoReport);
+        AppraisalStatus result = CertificateAttributeScvValidator
+                .validatePlatformCredentialAttributesV2p0(platformCredential,
+                        deviceInfoReport);
+        assertEquals(AppraisalStatus.Status.PASS, result.getAppStatus());
+        assertEquals(SupplyChainCredentialValidator.PLATFORM_ATTRIBUTES_VALID,
+                result.getMessage());
+        when(platformCredential.getManufacturer()).thenReturn(null);
+        result = CertificateAttributeScvValidator
+                .validatePlatformCredentialAttributesV2p0(platformCredential,
+                        deviceInfoReport);
+        assertEquals(AppraisalStatus.Status.FAIL, result.getAppStatus());
+        assertEquals("Platform manufacturer did not match\n", result.getMessage());
+
+        platformCredential = setupMatchingPlatformCredential(deviceInfoReport);
+        result = CertificateAttributeScvValidator
+                .validatePlatformCredentialAttributesV2p0(platformCredential,
+                        deviceInfoReport);
+        assertEquals(AppraisalStatus.Status.PASS, result.getAppStatus());
+        assertEquals(SupplyChainCredentialValidator.PLATFORM_ATTRIBUTES_VALID,
+                result.getMessage());
+        when(platformCredential.getModel()).thenReturn(null);
+        result = CertificateAttributeScvValidator
+                .validatePlatformCredentialAttributesV2p0(platformCredential,
+                        deviceInfoReport);
+        assertEquals(result.getAppStatus(), AppraisalStatus.Status.FAIL);
+        assertEquals(result.getMessage(), "Platform model did not match\n");
+
+        platformCredential = setupMatchingPlatformCredential(deviceInfoReport);
+        result = CertificateAttributeScvValidator
+                .validatePlatformCredentialAttributesV2p0(platformCredential,
+                        deviceInfoReport);
+        assertEquals(AppraisalStatus.Status.PASS, result.getAppStatus());
+        assertEquals(SupplyChainCredentialValidator.PLATFORM_ATTRIBUTES_VALID,
+                result.getMessage());
+        when(platformCredential.getPlatformSerial()).thenReturn(null);
+        result = CertificateAttributeScvValidator
+                .validatePlatformCredentialAttributesV2p0(platformCredential,
+                        deviceInfoReport);
+        assertEquals(AppraisalStatus.Status.PASS, result.getAppStatus());
+        platformCredential = setupMatchingPlatformCredential(deviceInfoReport);
+        result = CertificateAttributeScvValidator
+                .validatePlatformCredentialAttributesV2p0(platformCredential,
+                        deviceInfoReport);
+        assertEquals(AppraisalStatus.Status.PASS, result.getAppStatus());
+        assertEquals(SupplyChainCredentialValidator.PLATFORM_ATTRIBUTES_VALID,
+                result.getMessage());
+        when(platformCredential.getVersion()).thenReturn(null);
+        result = CertificateAttributeScvValidator
+                .validatePlatformCredentialAttributesV2p0(platformCredential,
+                        deviceInfoReport);
+        assertEquals(AppraisalStatus.Status.FAIL, result.getAppStatus());
+        assertEquals("Platform version did not match\n", result.getMessage());
+
+        platformCredential = setupMatchingPlatformCredential(deviceInfoReport);
+        result = CertificateAttributeScvValidator
+                .validatePlatformCredentialAttributesV2p0(platformCredential,
+                        deviceInfoReport);
+        assertEquals(AppraisalStatus.Status.PASS, result.getAppStatus());
+        assertEquals(SupplyChainCredentialValidator.PLATFORM_ATTRIBUTES_VALID,
+                result.getMessage());
+        List<ComponentIdentifier> modifiedComponentIdentifiers
+                = platformCredential.getComponentIdentifiers();
+        modifiedComponentIdentifiers.get(0).setComponentManufacturer(null);
+        when(platformCredential.getComponentIdentifiers()).thenReturn(modifiedComponentIdentifiers);
+        result = CertificateAttributeScvValidator
+                .validatePlatformCredentialAttributesV2p0(platformCredential,
+                        deviceInfoReport);
+        assertEquals(AppraisalStatus.Status.FAIL, result.getAppStatus());
+        assertEquals("Component manufacturer is empty\n", result.getMessage());
+
+        platformCredential = setupMatchingPlatformCredential(deviceInfoReport);
+        result = CertificateAttributeScvValidator
+                .validatePlatformCredentialAttributesV2p0(platformCredential,
+                        deviceInfoReport);
+        assertEquals(AppraisalStatus.Status.PASS, result.getAppStatus());
+        assertEquals(SupplyChainCredentialValidator.PLATFORM_ATTRIBUTES_VALID,
+                result.getMessage());
+        modifiedComponentIdentifiers = platformCredential.getComponentIdentifiers();
+        modifiedComponentIdentifiers.get(0).setComponentModel(null);
+        when(platformCredential.getComponentIdentifiers()).thenReturn(modifiedComponentIdentifiers);
+        result = CertificateAttributeScvValidator
+                .validatePlatformCredentialAttributesV2p0(platformCredential,
+                        deviceInfoReport);
+        assertEquals(AppraisalStatus.Status.FAIL, result.getAppStatus());
+        assertEquals("Component model is empty\n", result.getMessage());
+
+    }
+
+    /**
+     * Tests that the SupplyChainCredentialValidator fails when required fields contain only empty
+     * strings.
+     * @throws IOException if unable to set up DeviceInfoReport from resource file
+     */
+    @Test
+    public final void testValidatePlatformCredentialAttributesV2p0RequiredFieldsEmpty()
+            throws IOException {
+        DeviceInfoReport deviceInfoReport = setupDeviceInfoReportWithComponents();
+
+        PlatformCredential platformCredential = setupMatchingPlatformCredential(deviceInfoReport);
+        AppraisalStatus result = CertificateAttributeScvValidator
+                .validatePlatformCredentialAttributesV2p0(platformCredential,
+                        deviceInfoReport);
+        assertEquals(AppraisalStatus.Status.PASS, result.getAppStatus());
+        assertEquals(SupplyChainCredentialValidator.PLATFORM_ATTRIBUTES_VALID,
+                result.getMessage());
+        when(platformCredential.getManufacturer()).thenReturn("");
+        result = CertificateAttributeScvValidator
+                .validatePlatformCredentialAttributesV2p0(platformCredential,
+                        deviceInfoReport);
+        assertEquals(AppraisalStatus.Status.FAIL, result.getAppStatus());
+        assertEquals("Platform manufacturer did not match\n", result.getMessage());
+
+        platformCredential = setupMatchingPlatformCredential(deviceInfoReport);
+        result = CertificateAttributeScvValidator
+                .validatePlatformCredentialAttributesV2p0(platformCredential,
+                        deviceInfoReport);
+        assertEquals(AppraisalStatus.Status.PASS, result.getAppStatus());
+        assertEquals(SupplyChainCredentialValidator.PLATFORM_ATTRIBUTES_VALID,
+                result.getMessage());
+        when(platformCredential.getModel()).thenReturn("");
+        result = CertificateAttributeScvValidator
+                .validatePlatformCredentialAttributesV2p0(platformCredential,
+                        deviceInfoReport);
+        assertEquals(AppraisalStatus.Status.FAIL, result.getAppStatus());
+        assertEquals("Platform model did not match\n", result.getMessage());
+
+        platformCredential = setupMatchingPlatformCredential(deviceInfoReport);
+        result = CertificateAttributeScvValidator
+                .validatePlatformCredentialAttributesV2p0(platformCredential,
+                        deviceInfoReport);
+        assertEquals(AppraisalStatus.Status.PASS, result.getAppStatus());
+        assertEquals(SupplyChainCredentialValidator.PLATFORM_ATTRIBUTES_VALID,
+                result.getMessage());
+        when(platformCredential.getPlatformSerial()).thenReturn("");
+        result = CertificateAttributeScvValidator
+                .validatePlatformCredentialAttributesV2p0(platformCredential,
+                        deviceInfoReport);
+        assertEquals(AppraisalStatus.Status.FAIL, result.getAppStatus());
+        assertEquals("Platform serial did not match\n", result.getMessage());
+
+        platformCredential = setupMatchingPlatformCredential(deviceInfoReport);
+        result = CertificateAttributeScvValidator
+                .validatePlatformCredentialAttributesV2p0(platformCredential,
+                        deviceInfoReport);
+        assertEquals(AppraisalStatus.Status.PASS, result.getAppStatus());
+        assertEquals(SupplyChainCredentialValidator.PLATFORM_ATTRIBUTES_VALID,
+                result.getMessage());
+        when(platformCredential.getVersion()).thenReturn("");
+        result = CertificateAttributeScvValidator
+                .validatePlatformCredentialAttributesV2p0(platformCredential,
+                        deviceInfoReport);
+        assertEquals(AppraisalStatus.Status.FAIL, result.getAppStatus());
+        assertEquals("Platform version did not match\n", result.getMessage());
+
+        platformCredential = setupMatchingPlatformCredential(deviceInfoReport);
+        result = CertificateAttributeScvValidator
+                .validatePlatformCredentialAttributesV2p0(platformCredential,
+                        deviceInfoReport);
+        assertEquals(AppraisalStatus.Status.PASS, result.getAppStatus());
+        assertEquals(SupplyChainCredentialValidator.PLATFORM_ATTRIBUTES_VALID,
+                result.getMessage());
+        List<ComponentIdentifier> modifiedComponentIdentifiers
+                = platformCredential.getComponentIdentifiers();
+        modifiedComponentIdentifiers.get(0).setComponentManufacturer(new DERUTF8String(""));
+        when(platformCredential.getComponentIdentifiers()).thenReturn(modifiedComponentIdentifiers);
+        result = CertificateAttributeScvValidator
+                .validatePlatformCredentialAttributesV2p0(platformCredential,
+                        deviceInfoReport);
+        assertEquals(AppraisalStatus.Status.FAIL, result.getAppStatus());
+        assertEquals("Component manufacturer is empty\n"
+                + "There are unmatched components:\n"
+                + "Manufacturer=, Model=Core i7, Serial=Not Specified,"
+                + " Revision=Intel(R) Core(TM) i7-4790 CPU @ 3.60GHz;\n",
+                result.getMessage());
+
+        platformCredential = setupMatchingPlatformCredential(deviceInfoReport);
+        result = CertificateAttributeScvValidator
+                .validatePlatformCredentialAttributesV2p0(platformCredential,
+                        deviceInfoReport);
+        assertEquals(AppraisalStatus.Status.PASS, result.getAppStatus());
+        assertEquals(SupplyChainCredentialValidator.PLATFORM_ATTRIBUTES_VALID,
+                result.getMessage());
+        modifiedComponentIdentifiers = platformCredential.getComponentIdentifiers();
+        modifiedComponentIdentifiers.get(0).setComponentModel(new DERUTF8String(""));
+        when(platformCredential.getComponentIdentifiers()).thenReturn(modifiedComponentIdentifiers);
+        result = CertificateAttributeScvValidator
+                .validatePlatformCredentialAttributesV2p0(platformCredential,
+                        deviceInfoReport);
+        assertEquals(AppraisalStatus.Status.FAIL, result.getAppStatus());
+        assertEquals("Component model is empty\n", result.getMessage());
+    }
+
+    /**
+     * Tests that {@link SupplyChainCredentialValidator} failes when a component exists in the
+     * platform credential, but not in the device info report.
+     * @throws IOException if unable to set up DeviceInfoReport from resource file
+     */
+    @Test
+    public final void testValidatePlatformCredentialAttributesV2p0MissingComponentInDeviceInfo()
+            throws IOException {
+        DeviceInfoReport deviceInfoReport = setupDeviceInfoReportWithComponents();
+
+        PlatformCredential platformCredential = setupMatchingPlatformCredential(deviceInfoReport);
+        AppraisalStatus result = CertificateAttributeScvValidator
+                .validatePlatformCredentialAttributesV2p0(platformCredential,
+                        deviceInfoReport);
+        assertEquals(AppraisalStatus.Status.PASS, result.getAppStatus());
+        assertEquals(SupplyChainCredentialValidator.PLATFORM_ATTRIBUTES_VALID,
+                result.getMessage());
+
+        List<ComponentIdentifier> modifiedComponentIdentifiers
+                = platformCredential.getComponentIdentifiers();
+        modifiedComponentIdentifiers.add(new ComponentIdentifier(
+                new DERUTF8String("ACME"),
+                new DERUTF8String("TNT"),
+                new DERUTF8String("2"),
+                new DERUTF8String("1.1"),
+                null,
+                ASN1Boolean.FALSE,
+                Collections.emptyList()
+        ));
+        when(platformCredential.getComponentIdentifiers()).thenReturn(
+                modifiedComponentIdentifiers
+        );
+        result = CertificateAttributeScvValidator
+                .validatePlatformCredentialAttributesV2p0(platformCredential,
+                        deviceInfoReport);
+        assertEquals(AppraisalStatus.Status.FAIL, result.getAppStatus());
+        assertEquals("There are unmatched components:\n"
+                + "Manufacturer=ACME, Model=TNT, Serial=2, Revision=1.1;\n",
+                result.getMessage());
+    }
+
+    /**
+     * Tests that SupplyChainCredentialValidator passes when everything matches but there are
+     * extra components in the device info report that are not represented in the platform
+     * credential.
+     * @throws IOException if unable to set up DeviceInfoReport from resource file
+     */
+    @Test
+    public final void testValidatePlatformCredentialAttributesV2p0ExtraComponentInDeviceInfo()
+            throws IOException {
+        PlatformCredential platformCredential = setupMatchingPlatformCredential(
+                setupDeviceInfoReportWithComponents(SAMPLE_PACCOR_OUTPUT_TXT));
+
+        // The device info report will contain one extra component.
+        DeviceInfoReport deviceInfoReport = setupDeviceInfoReportWithComponents(
+                SAMPLE_PACCOR_OUTPUT_WITH_EXTRA_COMPONENT_TXT);
+
+        AppraisalStatus result = CertificateAttributeScvValidator
+                .validatePlatformCredentialAttributesV2p0(platformCredential,
+                        deviceInfoReport);
+        assertEquals(AppraisalStatus.Status.PASS, result.getAppStatus());
+        assertEquals(SupplyChainCredentialValidator.PLATFORM_ATTRIBUTES_VALID,
+                result.getMessage());
+
+        result = CertificateAttributeScvValidator
+                .validatePlatformCredentialAttributesV2p0(platformCredential,
+                        deviceInfoReport);
+        assertEquals(AppraisalStatus.Status.PASS, result.getAppStatus());
+        assertEquals(SupplyChainCredentialValidator.PLATFORM_ATTRIBUTES_VALID,
+                result.getMessage());
+    }
+
+    /**
+     * Tests that SupplyChainCredentialValidator fails when a component is found in the platform
+     * credential without a manufacturer or model.
+     * @throws IOException if unable to set up DeviceInfoReport from resource file
+     */
+    @Test
+    public final void testValidatePlatformCredentialAttributesV2p0RequiredComponentFieldEmpty()
+            throws IOException {
+        DeviceInfoReport deviceInfoReport = setupDeviceInfoReportWithComponents();
+
+        PlatformCredential platformCredential = setupMatchingPlatformCredential(deviceInfoReport);
+        AppraisalStatus result = CertificateAttributeScvValidator
+                .validatePlatformCredentialAttributesV2p0(platformCredential,
+                        deviceInfoReport);
+        assertEquals(AppraisalStatus.Status.PASS, result.getAppStatus());
+        assertEquals(SupplyChainCredentialValidator.PLATFORM_ATTRIBUTES_VALID,
+                result.getMessage());
+
+        List<ComponentIdentifier> componentIdentifiers
+                = platformCredential.getComponentIdentifiers();
+        componentIdentifiers.get(0).setComponentManufacturer(new DERUTF8String(""));
+        when(platformCredential.getComponentIdentifiers()).thenReturn(componentIdentifiers);
+
+        result = CertificateAttributeScvValidator
+                .validatePlatformCredentialAttributesV2p0(platformCredential,
+                        deviceInfoReport);
+        assertEquals(AppraisalStatus.Status.FAIL, result.getAppStatus());
+        assertEquals("Component manufacturer is empty\n"
+                + "There are unmatched components:\n"
+                + "Manufacturer=, Model=Core i7, Serial=Not Specified,"
+                + " Revision=Intel(R) Core(TM) i7-4790 CPU @ 3.60GHz;\n",
+                result.getMessage());
+
+        platformCredential = setupMatchingPlatformCredential(deviceInfoReport);
+        result = CertificateAttributeScvValidator
+                .validatePlatformCredentialAttributesV2p0(platformCredential,
+                        deviceInfoReport);
+        assertEquals(AppraisalStatus.Status.PASS, result.getAppStatus());
+        assertEquals(SupplyChainCredentialValidator.PLATFORM_ATTRIBUTES_VALID,
+                result.getMessage());
+
+        componentIdentifiers = platformCredential.getComponentIdentifiers();
+        componentIdentifiers.get(0).setComponentModel(null);
+        when(platformCredential.getComponentIdentifiers()).thenReturn(componentIdentifiers);
+
+        result = CertificateAttributeScvValidator
+                .validatePlatformCredentialAttributesV2p0(platformCredential,
+                        deviceInfoReport);
+        assertEquals(AppraisalStatus.Status.FAIL, result.getAppStatus());
+        assertEquals("Component model is empty\n", result.getMessage());
+    }
+
+    /**
+     * Tests that SupplyChainCredentialValidator passes when a component on the system has a
+     * matching component in the platform certificate, except the serial value is missing.
+     * @throws IOException if unable to set up DeviceInfoReport from resource file
+     */
+    @Test
+    public final void testValidatePlatformCredentialAttributesV2p0RequiredComponentNoSerial()
+            throws IOException {
+        DeviceInfoReport deviceInfoReport = setupDeviceInfoReportWithComponents();
+        PlatformCredential platformCredential = setupMatchingPlatformCredential(deviceInfoReport);
+
+        ArrayList<ComponentIdentifier> modifiedIdentifiers = new ArrayList<>();
+        for (ComponentIdentifier identifier : platformCredential.getComponentIdentifiers()) {
+            if (identifier.getComponentSerial() != null
+                    && identifier.getComponentSerial().toString().equals("23:94:17:ba:86:5e")) {
+                identifier.setComponentSerial(new DERUTF8String(""));
+            }
+            modifiedIdentifiers.add(identifier);
+        }
+
+        when(platformCredential.getComponentIdentifiers()).thenReturn(modifiedIdentifiers);
+
+        AppraisalStatus result = CertificateAttributeScvValidator
+                .validatePlatformCredentialAttributesV2p0(platformCredential,
+                        deviceInfoReport);
+        assertEquals(AppraisalStatus.Status.PASS, result.getAppStatus());
+        assertEquals(SupplyChainCredentialValidator.PLATFORM_ATTRIBUTES_VALID,
+                result.getMessage());
+    }
+
+    /**
+     * Tests that SupplyChainCredentialValidator passes when a component on the system has a
+     * matching component in the platform certificate, except the revision value is missing.
+     * @throws IOException if unable to set up DeviceInfoReport from resource file
+     */
+    @Test
+    public final void testValidatePlatformCredentialAttributesV2p0RequiredComponentNoRevision()
+            throws IOException {
+        DeviceInfoReport deviceInfoReport = setupDeviceInfoReportWithComponents();
+        PlatformCredential platformCredential = setupMatchingPlatformCredential(deviceInfoReport);
+
+        ArrayList<ComponentIdentifier> modifiedIdentifiers = new ArrayList<>();
+        for (ComponentIdentifier identifier : platformCredential.getComponentIdentifiers()) {
+            if (identifier.getComponentRevision() != null
+                    && identifier.getComponentRevision().toString().equals("00")) {
+                identifier.setComponentSerial(new DERUTF8String(""));
+            }
+            modifiedIdentifiers.add(identifier);
+        }
+
+        when(platformCredential.getComponentIdentifiers()).thenReturn(modifiedIdentifiers);
+
+        AppraisalStatus result = CertificateAttributeScvValidator
+                .validatePlatformCredentialAttributesV2p0(platformCredential,
+                        deviceInfoReport);
+        assertEquals(AppraisalStatus.Status.PASS, result.getAppStatus());
+        assertEquals(SupplyChainCredentialValidator.PLATFORM_ATTRIBUTES_VALID,
+                result.getMessage());
+    }
+
+    /**
+     * Tests that SupplyChainCredentialValidator passes when a component on the system has a
+     * matching component in the platform certificate, except the serial and revision values
+     * are missing.
+     * @throws IOException if unable to set up DeviceInfoReport from resource file
+     */
+    @Test
+    public final void testValPlatCredentialAttributesV2p0RequiredComponentNoSerialOrRevision()
+            throws IOException {
+        DeviceInfoReport deviceInfoReport = setupDeviceInfoReportWithComponents();
+        PlatformCredential platformCredential = setupMatchingPlatformCredential(deviceInfoReport);
+
+        ArrayList<ComponentIdentifier> modifiedIdentifiers = new ArrayList<>();
+        for (ComponentIdentifier identifier : platformCredential.getComponentIdentifiers()) {
+            if (identifier.getComponentSerial() != null
+                    && identifier.getComponentSerial().toString().equals("23:94:17:ba:86:5e")) {
+                identifier.setComponentSerial(new DERUTF8String(""));
+                identifier.setComponentRevision(new DERUTF8String(""));
+            }
+            modifiedIdentifiers.add(identifier);
+        }
+
+        when(platformCredential.getComponentIdentifiers()).thenReturn(modifiedIdentifiers);
+
+        AppraisalStatus result = CertificateAttributeScvValidator
+                .validatePlatformCredentialAttributesV2p0(platformCredential,
+                        deviceInfoReport);
+        assertEquals(AppraisalStatus.Status.PASS, result.getAppStatus());
+        assertEquals(SupplyChainCredentialValidator.PLATFORM_ATTRIBUTES_VALID,
+                result.getMessage());
+    }
+
+    /**
+     * Tests that SupplyChainCredentialValidator passes with a base and delta certificate where
+     * the base serial number and delta holder serial number match.
+     * @throws java.io.IOException Reading file for the certificates
+     * @throws java.net.URISyntaxException when loading certificates bytes
+     */
+    @Test
+    public final void testValidateDeltaPlatformCredentialAttributes()
+            throws IOException, URISyntaxException {
+        DeviceInfoReport deviceInfoReport = setupDeviceInfoReportWithComponents(
+                SAMPLE_PACCOR_OUTPUT_TXT);
+
+        PlatformCredential base = mock(PlatformCredential.class);
+        PlatformCredential delta1 = mock(PlatformCredential.class);
+        PlatformCredential delta2 = mock(PlatformCredential.class);
+
+        ComponentIdentifierV2 compId1 = new ComponentIdentifierV2(
+                new ComponentClass(Paths.get(Objects.requireNonNull(this.getClass()
+                        .getResource(JSON_FILE)).toURI()), "0x00010002"),
+                new DERUTF8String("Intel"),
+                new DERUTF8String("Core i7"), new DERUTF8String("Not Specified"),
+                new DERUTF8String("Intel(R) Core(TM) i7-4790 CPU @ 3.60GHz"), null,
+                ASN1Boolean.TRUE, new ArrayList<>(0), null, null,
+                null);
+        ComponentIdentifierV2 compId2 = new ComponentIdentifierV2(
+                new ComponentClass(Paths.get(Objects.requireNonNull(this.getClass()
+                        .getResource(JSON_FILE)).toURI()), "0x00050004"),
+                new DERUTF8String("Intel Corporation"),
+                new DERUTF8String("Ethernet Connection I217-V-faulty"),
+                new DERUTF8String("23:94:17:ba:86:5e"), new DERUTF8String("00"), null,
+                ASN1Boolean.FALSE, new ArrayList<>(0), null, null,
+                null);
+        ComponentIdentifierV2 compId3 = new ComponentIdentifierV2(
+                new ComponentClass(Paths.get(Objects.requireNonNull(this.getClass()
+                        .getResource(JSON_FILE)).toURI()), "0x00090002"),
+                new DERUTF8String("Intel Corporation"),
+                new DERUTF8String("82580 Gigabit Network Connection-faulty"),
+                new DERUTF8String("90:e2:ba:31:83:10"), new DERUTF8String(""), null,
+                ASN1Boolean.FALSE, new ArrayList<>(0), null, null,
+                null);
+        ComponentIdentifierV2 deltaCompId2 = new ComponentIdentifierV2(
+                new ComponentClass(Paths.get(Objects.requireNonNull(this.getClass()
+                        .getResource(JSON_FILE)).toURI()), "0x00050004"),
+                new DERUTF8String("Intel Corporation"),
+                new DERUTF8String("Ethernet Connection I217-V"),
+                new DERUTF8String("23:94:17:ba:86:5e"), new DERUTF8String("00"), null,
+                ASN1Boolean.FALSE, new ArrayList<>(0), null, null,
+                AttributeStatus.ADDED);
+        ComponentIdentifierV2 deltaCompId3 = new ComponentIdentifierV2(
+                new ComponentClass(Paths.get(Objects.requireNonNull(this.getClass()
+                        .getResource(JSON_FILE)).toURI()), "0x00090002"),
+                new DERUTF8String("Intel Corporation"),
+                new DERUTF8String("82580 Gigabit Network Connection"),
+                new DERUTF8String("90:e2:ba:31:83:10"), new DERUTF8String(""), null,
+                ASN1Boolean.FALSE, new ArrayList<>(0), null, null,
+                AttributeStatus.ADDED);
+
+        ComponentIdentifierV2 ciV21Faulty = new ComponentIdentifierV2();
+        ComponentIdentifierV2 ciV22Faulty = new ComponentIdentifierV2();
+        ciV21Faulty.setComponentManufacturer(compId2.getComponentManufacturer());
+        ciV21Faulty.setComponentClass(compId2.getComponentClass());
+        ciV21Faulty.setComponentModel(compId2.getComponentModel());
+        ciV21Faulty.setComponentSerial(compId2.getComponentSerial());
+        ciV21Faulty.setComponentRevision(compId2.getComponentRevision());
+        ciV21Faulty.setComponentManufacturerId(compId2.getComponentManufacturerId());
+        ciV21Faulty.setFieldReplaceable(compId2.getFieldReplaceable());
+        ciV21Faulty.setComponentAddress(compId2.getComponentAddress());
+        ciV21Faulty.setAttributeStatus(AttributeStatus.REMOVED);
+        ciV22Faulty.setComponentManufacturer(compId3.getComponentManufacturer());
+        ciV22Faulty.setComponentClass(compId3.getComponentClass());
+        ciV22Faulty.setComponentModel(compId3.getComponentModel());
+        ciV22Faulty.setComponentSerial(compId3.getComponentSerial());
+        ciV22Faulty.setComponentRevision(compId3.getComponentRevision());
+        ciV22Faulty.setComponentManufacturerId(compId3.getComponentManufacturerId());
+        ciV22Faulty.setFieldReplaceable(compId3.getFieldReplaceable());
+        ciV22Faulty.setComponentAddress(compId3.getComponentAddress());
+        ciV22Faulty.setAttributeStatus(AttributeStatus.REMOVED);
+
+        List<ComponentIdentifier> compList = new ArrayList<>(3);
+        compList.add(compId1);
+        compList.add(compId2);
+        compList.add(compId3);
+
+        List<ComponentIdentifier> delta1List = new ArrayList<>(2);
+        delta1List.add(ciV21Faulty);
+        delta1List.add(deltaCompId2);
+        List<ComponentIdentifier> delta2List = new ArrayList<>(2);
+        delta1List.add(ciV22Faulty);
+        delta1List.add(deltaCompId3);
+
+        when(base.isPlatformBase()).thenReturn(true);
+        when(delta1.isPlatformBase()).thenReturn(false);
+        when(delta2.isPlatformBase()).thenReturn(false);
+        when(base.getManufacturer()).thenReturn("innotek GmbH");
+        when(base.getModel()).thenReturn("VirtualBox");
+        when(base.getVersion()).thenReturn("1.2");
+        when(base.getPlatformSerial()).thenReturn("62UIAE5");
+        when(delta1.getPlatformSerial()).thenReturn("62UIAE5");
+        when(delta2.getPlatformSerial()).thenReturn("62UIAE5");
+        when(base.getPlatformChainType()).thenReturn("base");
+        when(delta1.getPlatformChainType()).thenReturn("delta");
+        when(delta2.getPlatformChainType()).thenReturn("delta");
+        when(base.getSerialNumber()).thenReturn(BigInteger.valueOf(01));
+        when(delta1.getSerialNumber()).thenReturn(BigInteger.valueOf(39821));
+        when(delta2.getSerialNumber()).thenReturn(BigInteger.valueOf(39822));
+        when(delta1.getHolderSerialNumber()).thenReturn(BigInteger.valueOf(02));
+        when(delta2.getHolderSerialNumber()).thenReturn(BigInteger.valueOf(39821));
+        when(base.getComponentIdentifiers()).thenReturn(compList);
+        when(delta1.getComponentIdentifiers()).thenReturn(delta1List);
+        when(delta2.getComponentIdentifiers()).thenReturn(delta2List);
+
+        Map<PlatformCredential, SupplyChainValidation> chainCredentials = new HashMap<>(0);
+        List<ArchivableEntity> certsUsed = new ArrayList<>();
+        certsUsed.add(base);
+        chainCredentials.put(base, new SupplyChainValidation(
+                SupplyChainValidation.ValidationType.PLATFORM_CREDENTIAL,
+                AppraisalStatus.Status.PASS, certsUsed, ""));
+        certsUsed.clear();
+        certsUsed.add(delta1);
+        chainCredentials.put(delta1, new SupplyChainValidation(
+                SupplyChainValidation.ValidationType.PLATFORM_CREDENTIAL,
+                AppraisalStatus.Status.PASS, certsUsed, ""));
+        certsUsed.clear();
+        certsUsed.add(delta2);
+        chainCredentials.put(delta2, new SupplyChainValidation(
+                SupplyChainValidation.ValidationType.PLATFORM_CREDENTIAL,
+                AppraisalStatus.Status.PASS, certsUsed, ""));
+
+        AppraisalStatus result = CertificateAttributeScvValidator
+                .validateDeltaPlatformCredentialAttributes(delta2,
+                        deviceInfoReport, base, chainCredentials);
+        assertEquals(AppraisalStatus.Status.PASS, result.getAppStatus());
+        assertEquals(SupplyChainCredentialValidator.PLATFORM_ATTRIBUTES_VALID,
+                result.getMessage());
+    }
+
+    /**
+     * Tests that SupplyChainCredentialValidator fails when a component needs to
+     * be replaced but hasn't been by a delta certificate.
+     * @throws java.io.IOException Reading file for the certificates
+     * @throws java.net.URISyntaxException when loading certificates bytes
+     */
+    @Test
+    public final void testValidateChainFailure()
+            throws IOException, URISyntaxException {
+        DeviceInfoReport deviceInfoReport = setupDeviceInfoReportWithComponents(
+                SAMPLE_PACCOR_OUTPUT_TXT);
+
+        PlatformCredential base = mock(PlatformCredential.class);
+        PlatformCredential delta1 = mock(PlatformCredential.class);
+
+        ComponentIdentifierV2 compId1 = new ComponentIdentifierV2(
+                new ComponentClass(Paths.get(Objects.requireNonNull(this.getClass()
+                        .getResource(JSON_FILE)).toURI()), "0x00010002"),
+                new DERUTF8String("Intel"),
+                new DERUTF8String("Core i7"), new DERUTF8String("Not Specified"),
+                new DERUTF8String("Intel(R) Core(TM) i7-4790 CPU @ 3.60GHz"), null,
+                ASN1Boolean.TRUE, new ArrayList<>(0), null, null,
+                null);
+        ComponentIdentifierV2 compId2 = new ComponentIdentifierV2(
+                new ComponentClass(Paths.get(Objects.requireNonNull(this.getClass()
+                        .getResource(JSON_FILE)).toURI()), "0x00050004"),
+                new DERUTF8String("Intel Corporation"),
+                new DERUTF8String("Ethernet Connection I217-V-faulty"),
+                new DERUTF8String("23:94:17:ba:86:5e"), new DERUTF8String("00"), null,
+                ASN1Boolean.FALSE, new ArrayList<>(0), null, null,
+                null);
+        ComponentIdentifierV2 compId3 = new ComponentIdentifierV2(
+                new ComponentClass(Paths.get(Objects.requireNonNull(this.getClass()
+                        .getResource(JSON_FILE)).toURI()), "0x00090002"),
+                new DERUTF8String("Intel Corporation"),
+                new DERUTF8String("82580 Gigabit Network Connection-faulty"),
+                new DERUTF8String("90:e2:ba:31:83:10"), new DERUTF8String(""), null,
+                ASN1Boolean.FALSE, new ArrayList<>(0), null, null,
+                null);
+        ComponentIdentifierV2 deltaCompId2 = new ComponentIdentifierV2(
+                new ComponentClass(Paths.get(Objects.requireNonNull(this.getClass()
+                        .getResource(JSON_FILE)).toURI()), "0x00050004"),
+                new DERUTF8String("Intel Corporation"),
+                new DERUTF8String("Ethernet Connection I217-V"),
+                new DERUTF8String("23:94:17:ba:86:5e"), new DERUTF8String("00"), null,
+                ASN1Boolean.FALSE, new ArrayList<>(0), null, null,
+                AttributeStatus.ADDED);
+
+        ComponentIdentifierV2 ciV21Faulty = new ComponentIdentifierV2();
+        ComponentIdentifierV2 ciV22Faulty = new ComponentIdentifierV2();
+        ciV21Faulty.setComponentManufacturer(compId2.getComponentManufacturer());
+        ciV21Faulty.setComponentModel(compId2.getComponentModel());
+        ciV21Faulty.setComponentSerial(compId2.getComponentSerial());
+        ciV21Faulty.setComponentRevision(compId2.getComponentRevision());
+        ciV21Faulty.setComponentManufacturerId(compId2.getComponentManufacturerId());
+        ciV21Faulty.setFieldReplaceable(compId2.getFieldReplaceable());
+        ciV21Faulty.setComponentAddress(compId2.getComponentAddress());
+        ciV21Faulty.setAttributeStatus(AttributeStatus.REMOVED);
+        ciV22Faulty.setComponentManufacturer(compId3.getComponentManufacturer());
+        ciV22Faulty.setComponentModel(compId3.getComponentModel());
+        ciV22Faulty.setComponentSerial(compId3.getComponentSerial());
+        ciV22Faulty.setComponentRevision(compId3.getComponentRevision());
+        ciV22Faulty.setComponentManufacturerId(compId3.getComponentManufacturerId());
+        ciV22Faulty.setFieldReplaceable(compId3.getFieldReplaceable());
+        ciV22Faulty.setComponentAddress(compId3.getComponentAddress());
+        ciV22Faulty.setAttributeStatus(AttributeStatus.REMOVED);
+
+        List<ComponentIdentifier> compList = new ArrayList<>(3);
+        compList.add(compId1);
+        compList.add(compId2);
+        compList.add(compId3);
+
+        List<ComponentIdentifier> delta1List = new ArrayList<>(2);
+        delta1List.add(ciV21Faulty);
+        delta1List.add(deltaCompId2);
+
+        when(base.isPlatformBase()).thenReturn(true);
+        when(delta1.isPlatformBase()).thenReturn(false);
+        when(base.getManufacturer()).thenReturn("innotek GmbH");
+        when(base.getModel()).thenReturn("VirtualBox");
+        when(base.getVersion()).thenReturn("1.2");
+        when(base.getPlatformSerial()).thenReturn("0");
+        when(delta1.getPlatformSerial()).thenReturn("0");
+        when(base.getPlatformChainType()).thenReturn("base");
+        when(delta1.getPlatformChainType()).thenReturn("delta");
+        when(base.getSerialNumber()).thenReturn(BigInteger.ZERO);
+        when(delta1.getSerialNumber()).thenReturn(BigInteger.ONE);
+        when(delta1.getHolderSerialNumber()).thenReturn(BigInteger.ZERO);
+        when(base.getComponentIdentifiers()).thenReturn(compList);
+        when(delta1.getComponentIdentifiers()).thenReturn(delta1List);
+
+        Map<PlatformCredential, SupplyChainValidation> chainCredentials = new HashMap<>(0);
+        List<ArchivableEntity> certsUsed = new ArrayList<>();
+        certsUsed.add(base);
+        chainCredentials.put(base, new SupplyChainValidation(
+                SupplyChainValidation.ValidationType.PLATFORM_CREDENTIAL,
+                AppraisalStatus.Status.PASS, certsUsed, ""));
+        certsUsed.clear();
+        certsUsed.add(delta1);
+        chainCredentials.put(delta1, new SupplyChainValidation(
+                SupplyChainValidation.ValidationType.PLATFORM_CREDENTIAL,
+                AppraisalStatus.Status.PASS, certsUsed, ""));
+
+        AppraisalStatus result = CertificateAttributeScvValidator
+                .validateDeltaPlatformCredentialAttributes(delta1,
+                        deviceInfoReport, base, chainCredentials);
+        assertEquals(AppraisalStatus.Status.FAIL, result.getAppStatus());
+        assertEquals("There are unmatched components:\n" +
+                "Manufacturer=Intel Corporation, Model=82580 Gigabit Network " +
+                "Connection-faulty, Serial=90:e2:ba:31:83:10, Revision=;\n",
+                result.getMessage());
+    }
+
+    /**
+     * Creates a new RSA 1024-bit KeyPair using a Bouncy Castle Provider.
+     *
+     * @return new KeyPair
+     */
+    private static KeyPair createKeyPair() {
+        final int keySize = 1024;
+        KeyPairGenerator gen;
+        KeyPair keyPair = null;
+        try {
+            gen = KeyPairGenerator.getInstance("RSA", BouncyCastleProvider.PROVIDER_NAME);
+            gen.initialize(keySize, SECURE_RANDOM);
+            keyPair = gen.generateKeyPair();
+        } catch (NoSuchAlgorithmException | NoSuchProviderException e) {
+            fail("Error occurred while generating key pair", e);
+        }
+        return keyPair;
+    }
+
+    /**
+     * Create a new X.509 attribute certificate given the holder cert, the signing cert, and the
+     * signing key.
+     *
+     * @param targetCert
+     *            X509Certificate that will be the holder of the attribute cert
+     * @param signingCert
+     *            X509Certificate used to sign the new attribute cert
+     * @param caPrivateKey
+     *            PrivateKey used to sign the new attribute cert
+     * @return new X509AttributeCertificate
+     */
+    private static X509AttributeCertificateHolder createAttributeCert(
+            final X509Certificate targetCert, final X509Certificate signingCert,
+            final PrivateKey caPrivateKey) {
+        X509AttributeCertificateHolder cert = null;
+        try {
+            final int timeRange = 50000;
+            AttributeCertificateHolder holder =
+                    new AttributeCertificateHolder(new X509CertificateHolder(
+                            targetCert.getEncoded()));
+            AttributeCertificateIssuer issuer =
+                    new AttributeCertificateIssuer(new X500Name(signingCert
+                            .getSubjectX500Principal().getName()));
+            BigInteger serialNumber = BigInteger.ONE;
+            Date notBefore = new Date(System.currentTimeMillis() - timeRange);
+            Date notAfter = new Date(System.currentTimeMillis() + timeRange);
+            X509v2AttributeCertificateBuilder builder =
+                    new X509v2AttributeCertificateBuilder(holder, issuer, serialNumber, notBefore,
+                            notAfter);
+
+            ContentSigner signer =
+                    new JcaContentSignerBuilder("SHA1WithRSA").setProvider("BC")
+                            .build(caPrivateKey);
+
+            cert = builder.build(signer);
+        } catch (CertificateEncodingException | IOException | OperatorCreationException e) {
+            fail("Exception occurred while creating a cert", e);
+        }
+
+        return cert;
+
+    }
+
+    /**
+     * Create a new X.509 public-key certificate signed by the given certificate.
+     *
+     * @param keyPair
+     *            KeyPair to create the cert for
+     * @param signingKey
+     *            PrivateKey of the signing cert
+     * @param signingCert
+     *            signing cert
+     * @return new X509Certificate
+     */
+    private static X509Certificate createCertSignedByAnotherCert(final KeyPair keyPair,
+                                                                 final PrivateKey signingKey, final X509Certificate signingCert) {
+        final int timeRange = 10000;
+        X509Certificate cert = null;
+        try {
+
+            X500Name issuerName = new X500Name(signingCert.getSubjectX500Principal().getName());
+            X500Name subjectName = new X500Name("CN=Test V3 Certificate");
+            BigInteger serialNumber = BigInteger.ONE;
+            Date notBefore = new Date(System.currentTimeMillis() - timeRange);
+            Date notAfter = new Date(System.currentTimeMillis() + timeRange);
+            X509v3CertificateBuilder builder =
+                    new JcaX509v3CertificateBuilder(issuerName, serialNumber, notBefore, notAfter,
+                            subjectName, keyPair.getPublic());
+            ContentSigner signer =
+                    new JcaContentSignerBuilder("SHA1WithRSA").setProvider("BC").build(signingKey);
+            return new JcaX509CertificateConverter().setProvider("BC").getCertificate(
+                    builder.build(signer));
+        } catch (Exception e) {
+            fail("Exception occurred while creating a cert", e);
+        }
+        return cert;
+    }
+
+    /**
+     * Creates a self-signed X.509 public-key certificate.
+     *
+     * @param pair
+     *            KeyPair to create the cert for
+     * @return self-signed X509Certificate
+     */
+    private static X509Certificate createSelfSignedCertificate(final KeyPair pair) {
+        Security.addProvider(new BouncyCastleProvider());
+        final int timeRange = 10000;
+        X509Certificate cert = null;
+        try {
+
+            X500Name issuerName = new X500Name("CN=Test Self-Signed V3 Certificate");
+            X500Name subjectName = new X500Name("CN=Test Self-Signed V3 Certificate");
+            BigInteger serialNumber = BigInteger.ONE;
+            Date notBefore = new Date(System.currentTimeMillis() - timeRange);
+            Date notAfter = new Date(System.currentTimeMillis() + timeRange);
+            X509v3CertificateBuilder builder =
+                    new JcaX509v3CertificateBuilder(issuerName, serialNumber, notBefore, notAfter,
+                            subjectName, pair.getPublic());
+            ContentSigner signer =
+                    new JcaContentSignerBuilder("SHA1WithRSA").setProvider("BC").build(
+                            pair.getPrivate());
+            return new JcaX509CertificateConverter().setProvider("BC").getCertificate(
+                    builder.build(signer));
+        } catch (Exception e) {
+            fail("Exception occurred while creating a cert", e);
+        }
+        return cert;
+    }
+
+    private DeviceInfoReport buildReport(final HardwareInfo hardwareInfo) {
+        final InetAddress ipAddress = getTestIpAddress();
+        final byte[] macAddress = new byte[] {11, 22, 33, 44, 55, 66};
+
+        OSInfo osInfo = new OSInfo();
+        NetworkInfo networkInfo = new NetworkInfo("test", ipAddress, macAddress);
+        FirmwareInfo firmwareInfo = new FirmwareInfo();
+        TPMInfo tpmInfo = new TPMInfo();
+
+        return new DeviceInfoReport(networkInfo, osInfo,
+                firmwareInfo, hardwareInfo, tpmInfo);
+    }
+    private static InetAddress getTestIpAddress() {
+        try {
+            return InetAddress.getByAddress(new byte[] {127, 0, 0, 1});
+        } catch (UnknownHostException e) {
+            return null;
+        }
+    }
+}
diff --git a/HIRS_AttestationCA/src/test/resources/hirs/validation/sample_paccor_output.txt b/HIRS_AttestationCA/src/test/resources/hirs/validation/sample_paccor_output.txt
new file mode 100755
index 00000000..d2a7d72c
--- /dev/null
+++ b/HIRS_AttestationCA/src/test/resources/hirs/validation/sample_paccor_output.txt
@@ -0,0 +1,36 @@
+{
+    
+    "PLATFORM": {
+        "PLATFORMMANUFACTURERSTR": "innotek GmbH","PLATFORMMODEL": "VirtualBox","PLATFORMVERSION": "1.2","PLATFORMSERIAL": "0"
+    },
+    "COMPONENTS": [
+        {
+            "COMPONENTCLASS": {
+                    "COMPONENTCLASSREGISTRY": "2.23.133.18.3.1",
+                    "COMPONENTCLASSVALUE": "00010002"
+                },"MANUFACTURER": "Intel","MODEL": "Core i7","SERIAL": "Not Specified","REVISION": "Intel(R) Core(TM) i7-4790 CPU @ 3.60GHz"
+        },
+        {
+            "COMPONENTCLASS": {
+                    "COMPONENTCLASSREGISTRY": "2.23.133.18.3.1",
+                    "COMPONENTCLASSVALUE": "00050004"
+                },"MANUFACTURER": "Intel Corporation","MODEL": "Ethernet Connection I217-V", "FIELDREPLACEABLE": "false","SERIAL": "23:94:17:ba:86:5e", "REVISION": "00"
+        },
+        {
+            "COMPONENTCLASS": {
+                    "COMPONENTCLASSREGISTRY": "2.23.133.18.3.1",
+                    "COMPONENTCLASSVALUE": "00090002"
+                },"MANUFACTURER": "Intel Corporation","MODEL": "82580 Gigabit Network Connection", "FIELDREPLACEABLE": "false", "SERIAL": "90:e2:ba:31:83:10", "REVISION": ""
+        }
+    ],
+    "PROPERTIES": [
+        {
+            "NAME": "uname -r",
+            "VALUE": "3.10.0-862.11.6.el7.x86_64"
+        },
+        {
+            "NAME": "cat /etc/centos-release",
+            "VALUE": "CentOS Linux release 7.5.1804 (Core) "
+        }
+    ]
+}
diff --git a/HIRS_AttestationCA/src/test/resources/hirs/validation/sample_paccor_output_not_specified_values.txt b/HIRS_AttestationCA/src/test/resources/hirs/validation/sample_paccor_output_not_specified_values.txt
new file mode 100755
index 00000000..54cf2ec4
--- /dev/null
+++ b/HIRS_AttestationCA/src/test/resources/hirs/validation/sample_paccor_output_not_specified_values.txt
@@ -0,0 +1,34 @@
+{
+    
+    "PLATFORM": {
+        "PLATFORMMANUFACTURERSTR": "Not Specified","PLATFORMMODEL": "Not Specified","PLATFORMVERSION": "Not Specified"
+    },
+    "COMPONENTS": [
+        {
+            "MANUFACTURER": "Not Specified","MODEL": "Not Specified"
+        },
+        {
+            "MANUFACTURER": "Not Specified","MODEL": "Not Specified","FIELDREPLACEABLE": "false"
+        },
+        {
+            "MANUFACTURER": "Not Specified","MODEL": "UEFI"
+        },
+       {
+           "MANUFACTURER": "Broadcom Inc. and subsidiaries","MODEL": "NetXtreme BCM5722 Gigabit Ethernet PCI Express","FIELDREPLACEABLE": "true","REVISION": "00"
+       },
+       {
+           "MANUFACTURER": "Intel Corporation","MANUFACTURERID": "1.3.6.1.4.1.343","MODEL": "Ethernet Connection (2) I219-LM","FIELDREPLACEABLE": "true","REVISION": "31"
+       }
+    ],
+    "PROPERTIES": [
+        {
+            "NAME": "uname -r",
+            "VALUE": "3.10.0-957.1.3.el7.x86_64"
+        },
+        {
+            "NAME": "OS Release",
+            "VALUE": "CentOS Linux 7 (Core)"
+        }
+    ]
+}
+
diff --git a/HIRS_AttestationCA/src/test/resources/hirs/validation/sample_paccor_output_with_extra_component.txt b/HIRS_AttestationCA/src/test/resources/hirs/validation/sample_paccor_output_with_extra_component.txt
new file mode 100755
index 00000000..9f58ecf2
--- /dev/null
+++ b/HIRS_AttestationCA/src/test/resources/hirs/validation/sample_paccor_output_with_extra_component.txt
@@ -0,0 +1,30 @@
+{
+    
+    "PLATFORM": {
+        "PLATFORMMANUFACTURERSTR": "innotek GmbH","PLATFORMMODEL": "VirtualBox","PLATFORMVERSION": "1.2","PLATFORMSERIAL": "0"
+    },
+    "COMPONENTS": [
+        {
+            "MANUFACTURER": "Intel","MODEL": "Core i7","SERIAL": "Not Specified","REVISION": "Intel(R) Core(TM) i7-4790 CPU @ 3.60GHz"
+        },
+        {
+            "MANUFACTURER": "Intel Corporation","MODEL": "Ethernet Connection I217-V", "FIELDREPLACEABLE": "false","SERIAL": "23:94:17:ba:86:5e", "REVISION": "00"
+        },
+        {
+            "MANUFACTURER": "Intel Corporation","MODEL": "82580 Gigabit Network Connection", "FIELDREPLACEABLE": "false", "SERIAL": "90:e2:ba:31:83:10", "REVISION": ""
+        },
+        {
+            "MANUFACTURER": "Intel","MODEL": "platform2018", "FIELDREPLACEABLE": "false", "SERIAL": "BQKP52840678", "REVISION": "1.0"
+        }
+    ],
+    "PROPERTIES": [
+        {
+            "NAME": "uname -r",
+            "VALUE": "3.10.0-862.11.6.el7.x86_64"
+        },
+        {
+            "NAME": "cat /etc/centos-release",
+            "VALUE": "CentOS Linux release 7.5.1804 (Core) "
+        }
+    ]
+}
diff --git a/HIRS_AttestationCA/src/test/resources/validation/platform_credentials/Intel_pc2.pem b/HIRS_AttestationCA/src/test/resources/validation/platform_credentials/Intel_pc2.pem
new file mode 100644
index 0000000000000000000000000000000000000000..9ea77f12c977877d6ba8cafe4de96fd59203dea6
GIT binary patch
literal 914
zcmXqLV(v3&V*1X+$Y@}&K+iyIiHL!ap@0D&8*?ZNGY@-kh;L?cQ9hFhJ40h@`hU|(
zu?d&1uuO^ln$4Gcd_m&`gT~$^ja>$f?S|Y2oFF}H!c3vTFb)R~le42C&_s|7I}dko
zVqQt2f^$w{QKF%gfdojDi$~BiuOu}`!8yOEAipTFBr`wHP|-jhB*)Do5mJ<xS6q--
zl$uwf5L{YNkW;DPoROHBXDDVM0#d@v!&_cnu9pciLN7T#*MOIeQ>)FR?K>|cBP%Nd
zlL*t~1Ha8xg86M;O4^)U{;$AM^6WMPB?*2b14DBI6C*<)X=-E{1rjheFf=j-B2!CK
z%P0dw14%a4^hQ=DK~QiQZ~{Y!iIE9N@-P|#?FLH;gFMQ_$k4>d%*e=K(8Rdjpm8nG
zS+WX@jQ?307a26pH)x#0#szZG4_`)BCKh0L8R)ZdXtM!b$IirLpv<Ef;vC?p5K>fH
zT#}li5Rj8tl9peTtKgcKl3!GunhW%}K@?mAqnKYtNl8JmmA-xnL`QLIQCVhkYB3@R
z^po>}p^;aj9}?`WpPX7$QVe2&7y<b?naP#GC5a_qbM*>R(hSN$&J_eXH`gG0Nv1(M
zIP$^1W8q;1M~$JVfiT3stUO#UuErLIrXi8u9tdGJ9xe|fGfPVgT@wQ%LsbK1Ha1|u
zu`;spNV=OvdYc#-8yGlSnCrN^hC~8`+`z!lJq#SfvdSzH24W2&8kaY`_^&emTMzT2
z?xQ?O>vVsmHyI?ue9g$n5^fM`5DeoRFtxdp6AEC@1ZSq_W#*-Or&j6#g9I(PFgG?b
zlrP@gb0hY_@=rc5vaFjA7w&j9#iGsT?QNFh8?&xXR<CY)t~+6S@wwwkEA!bG=W-o*
z*VwshM|DEAQ^)5n@g33?TVy#KO%%hY9g|B_^5EF~c#8Cu{>=<&2Ly{>@N6|ay049I
k{ln^v$xGV5{t48Miz(gH|1E!NiAckd+ZJla{2X5a0Fet4>i_@%

literal 0
HcmV?d00001

diff --git a/HIRS_AttestationCA/src/test/resources/validation/platform_credentials/pciids_plat_cert_2-0.pem b/HIRS_AttestationCA/src/test/resources/validation/platform_credentials/pciids_plat_cert_2-0.pem
new file mode 100644
index 00000000..dcc74430
--- /dev/null
+++ b/HIRS_AttestationCA/src/test/resources/validation/platform_credentials/pciids_plat_cert_2-0.pem
@@ -0,0 +1,37 @@
+-----BEGIN ATTRIBUTE CERTIFICATE-----
+MIIHuzCCBqMCAQEwc6BxMFmkVzBVMQswCQYDVQQGEwJDSDEeMBwGA1UEChMVU1RNaWNyb2VsZWN0
+cm9uaWNzIE5WMSYwJAYDVQQDEx1TVE0gVFBNIEVLIEludGVybWVkaWF0ZSBDQSAwMgIUS5gujeW5
+kYvYdMJZlIUT6s3F0cygOjA4pDYwNDELMAkGA1UEBhMCVVMxFDASBgNVBAoMC2V4YW1wbGUuY29t
+MQ8wDQYDVQQLDAZQQ1Rlc3QwDQYJKoZIhvcNAQELBQACAQEwIhgPMjAxODAxMDEwNTAwMDBaGA8y
+MDI4MDEwMTA1MDAwMFowggRkMAsGBWeBBQITMQIwADAcBgVngQUCETETMBEwCQIBAQIBAwIBFgQE
+AAAAATASBgVngQUCGTEJMAcGBWeBBQgCMIIECwYHZ4EFBQEHAjGCA/4wggP6oIID9DBbMA4GBmeB
+BRIDAQQEAAIAAQwWVG8gQmUgRmlsbGVkIEJ5IE8uRS5NLgwBM4AWVG8gQmUgRmlsbGVkIEJ5IE8u
+RS5NLoEWVG8gQmUgRmlsbGVkIEJ5IE8uRS5NLjAoMA4GBmeBBRIDAQQEAAMAAwwGQVNSb2NrDAtY
+NTggRXh0cmVtZYMB/zBAMA4GBmeBBRIDAQQEABMAAwwYQW1lcmljYW4gTWVnYXRyZW5kcyBJbmMu
+DA1Ob3QgU3BlY2lmaWVkgQVQMi45MDBoMA4GBmeBBRIDAQQEAAEAAgwFSW50ZWwMAzE5OIAWVG8g
+QmUgRmlsbGVkIEJ5IE8uRS5NLoEvSW50ZWwoUikgQ29yZShUTSkgaTcgQ1BVICAgICAgICAgOTIw
+ICBAIDIuNjdHSHqDAf8wSjAOBgZngQUSAwEEBAAGAAEMDk1hbnVmYWN0dXJlcjAwDA1PQ1ozRzE2
+MDBMVjJHgAgwMDAwMDAwMIEMQXNzZXRUYWdOdW0wgwH/MEowDgYGZ4EFEgMBBAQABgABDA5NYW51
+ZmFjdHVyZXIwMQwNT0NaM0cxNjAwTFYyR4AIMDAwMDAwMDCBDEFzc2V0VGFnTnVtMYMB/zBKMA4G
+BmeBBRIDAQQEAAYAAQwOTWFudWZhY3R1cmVyMDIMDU5vdCBTcGVjaWZpZWSACDAwMDAwMDAwgQxB
+c3NldFRhZ051bTKDAf8wSjAOBgZngQUSAwEEBAAGAAEMDk1hbnVmYWN0dXJlcjAzDA1PQ1ozRzE2
+MDBMVjJHgAgwMDAwMDAwMIEMQXNzZXRUYWdOdW0zgwH/MEowDgYGZ4EFEgMBBAQABgABDA5NYW51
+ZmFjdHVyZXIwNAwNT0NaM0cxNjAwTFYyR4AIMDAwMDAwMDCBDEFzc2V0VGFnTnVtNIMB/zBKMA4G
+BmeBBRIDAQQEAAYAAQwOTWFudWZhY3R1cmVyMDUMDU5vdCBTcGVjaWZpZWSACDAwMDAwMDAwgQxB
+c3NldFRhZ051bTWDAf8wSjAOBgZngQUSAwEEBAAJAAIMBDgwODYMBDI0RjOADEE0MzREOTEyMzQ1
+NoECM0GDAf+kFzAVBgVngQURAgwMQTQzNEQ5MTIzNDU2MEowDgYGZ4EFEgMBBAQACQACDAQxMEVD
+DAQ4MTY4gAwwMDE5NjZBQkNERUaBAjAzgwH/pBcwFQYFZ4EFEQEMDDAwMTk2NkFCQ0RFRjA6MA4G
+BmeBBRIDAQQEAAcAAgwNTm90IFNwZWNpZmllZAwMU1QzMTUwMDM0MUFTgAg4WDY4WTMyMIMB/zAj
+MA4GBmeBBRIDAQQEAAUAAgwEMTAwMgwENjg5OYECMDCDAf+iADAUBgVngQUCFzELMAkCAQECAQEC
+AREwggFNMGQGA1UdIwRdMFuAFGQP4SIG+UWEJp5BdqBD3dUDaRgOoTikNjA0MQswCQYDVQQGEwJV
+UzEUMBIGA1UECgwLZXhhbXBsZS5jb20xDzANBgNVBAsMBlBDVGVzdIIJAISFLMl6DJA8MEEGA1Ud
+IAQ6MDgwNgYCKgMwMDAuBggrBgEFBQcCAjAiDCBUQ0cgVHJ1c3RlZCBQbGF0Zm9ybSBFbmRvcnNl
+bWVudDCBoQYDVR0RBIGZMIGWpIGTMIGQMSIwIAYGZ4EFBQEEDBZUbyBCZSBGaWxsZWQgQnkgTy5F
+Lk0uMSIwIAYGZ4EFBQEBDBZUbyBCZSBGaWxsZWQgQnkgTy5FLk0uMSIwIAYGZ4EFBQEFDBZUbyBC
+ZSBGaWxsZWQgQnkgTy5FLk0uMSIwIAYGZ4EFBQEGDBZUbyBCZSBGaWxsZWQgQnkgTy5FLk0uMA0G
+CSqGSIb3DQEBCwUAA4IBAQCiJcOtpVn43jbGkEhNq0rfdtnvnn9/N99eNeYO2+jGbKOQDkC1TxYO
+QXgaWl32KVc9q044KX4062tt2cQHIwFDK7dPLAaUkCJ8x7mjg7Np7ddzqWHtkAyr+USntdjf0o/z
+8Ru5aUSVBA0sphpRN66nVU8sGKSf31CZhSBMpBCToKyil+eFUF3n6X2Z9fjhzermoPVNqkff7/Ai
+cldsbnTb46CGdQSWhctw7sbyy9B9VTYbqDMfMQdpifl2JQBkXaC7XPe9Z6J8VJVWiTh91be5JSAd
+Uyq5/X2IajIEGp8OP+zQSaStT2RaoeN1VdmPGrv87YbUs9buKTpTSYNZwI2d
+-----END ATTRIBUTE CERTIFICATE-----

From de84ccbb2cdaf0970117a762725474605a1048d7 Mon Sep 17 00:00:00 2001
From: iadgovuser62 <iadgovuser62@empire.eclipse.ncsc.mil>
Date: Fri, 23 Feb 2024 12:53:13 -0500
Subject: [PATCH 3/3] Adding tests for validating mismatching
 baseboard/chassis/system serial numbers in DeviceInfoReport objects

---
 .../SupplyChainCredentialValidatorTest.java   | 92 ++++++++++++++++++-
 1 file changed, 91 insertions(+), 1 deletion(-)

diff --git a/HIRS_AttestationCA/src/test/java/hirs/attestationca/persist/validation/SupplyChainCredentialValidatorTest.java b/HIRS_AttestationCA/src/test/java/hirs/attestationca/persist/validation/SupplyChainCredentialValidatorTest.java
index 16a5dd2c..00aaeeca 100644
--- a/HIRS_AttestationCA/src/test/java/hirs/attestationca/persist/validation/SupplyChainCredentialValidatorTest.java
+++ b/HIRS_AttestationCA/src/test/java/hirs/attestationca/persist/validation/SupplyChainCredentialValidatorTest.java
@@ -93,7 +93,10 @@ import java.util.Map;
 import java.util.HashMap;
 
 /**
- * Tests the SupplyChainValidator class.
+ * Tests the SupplyChainCredentialValidator and CredentialValidator class.
+ * Migration note: Tests specifically for test Intel Nuc Platform Credentials
+ * have been omitted, as there is no existing matching test Endorsement Credential
+ * in the project resources.
  */
 public class SupplyChainCredentialValidatorTest {
 
@@ -436,6 +439,93 @@ public class SupplyChainCredentialValidatorTest {
                 result.getMessage());
     }
 
+    /**
+     * Checks if validation occurs when the Platform Credential baseboard
+     * serial number is in the device chassis serial number field.
+     */
+    @Test
+    public final void validatePlatformCredentialCombinedWithChassisSerialNumbersMatchedBaseboard()
+            throws Exception {
+
+        DeviceInfoReport deviceInfoReport = buildReport(new HardwareInfo(
+                DeviceInfoEnums.NOT_SPECIFIED, DeviceInfoEnums.NOT_SPECIFIED,
+                DeviceInfoEnums.NOT_SPECIFIED, DeviceInfoEnums.NOT_SPECIFIED,
+                TEST_BOARD_SERIAL_NUMBER, DeviceInfoEnums.NOT_SPECIFIED));
+
+        byte[] certBytes = Files.readAllBytes(Paths.get(Objects.requireNonNull(CertificateTest.class.
+                getResource(INTEL_PLATFORM_CERT_2)).toURI()));
+
+        PlatformCredential pc = new PlatformCredential(certBytes);
+
+        EndorsementCredential ec = new EndorsementCredential(
+                Files.readAllBytes(Paths.get(Objects.requireNonNull(getClass().getResource(TEST_EK_CERT)).toURI())));
+
+        AppraisalStatus result =
+                CredentialValidator.validatePlatformCredentialAttributes(pc,
+                        deviceInfoReport, ec);
+        assertEquals(AppraisalStatus.Status.PASS, result.getAppStatus());
+        assertEquals(SupplyChainCredentialValidator.PLATFORM_ATTRIBUTES_VALID,
+                result.getMessage());
+    }
+
+    /**
+     * Checks if validation occurs when the Platform Credential chassis
+     * serial number is in the device baseboard serial number field.
+     */
+    @Test
+    public final void validatePlatformCredentialCombinedWithBaseboardSerialNumbersMatchedChassis()
+            throws Exception {
+
+        DeviceInfoReport deviceInfoReport = buildReport(new HardwareInfo(
+                DeviceInfoEnums.NOT_SPECIFIED, DeviceInfoEnums.NOT_SPECIFIED,
+                DeviceInfoEnums.NOT_SPECIFIED, DeviceInfoEnums.NOT_SPECIFIED,
+                DeviceInfoEnums.NOT_SPECIFIED, TEST_CHASSIS_SERIAL_NUMBER));
+
+        byte[] certBytes = Files.readAllBytes(Paths.get(Objects.requireNonNull(CertificateTest.class.
+                getResource(INTEL_PLATFORM_CERT_2)).toURI()));
+
+        PlatformCredential pc = new PlatformCredential(certBytes);
+
+        EndorsementCredential ec = new EndorsementCredential(
+                Files.readAllBytes(Paths.get(Objects.requireNonNull(getClass().getResource(TEST_EK_CERT)).toURI())));
+
+        AppraisalStatus result =
+                CredentialValidator.validatePlatformCredentialAttributes(pc,
+                        deviceInfoReport, ec);
+        assertEquals(AppraisalStatus.Status.PASS, result.getAppStatus());
+        assertEquals(SupplyChainCredentialValidator.PLATFORM_ATTRIBUTES_VALID,
+                result.getMessage());
+    }
+
+    /**
+     * Checks if validation occurs when the Platform Credential chassis
+     * serial number is in the device system serial number field.
+     */
+    @Test
+    public final void validatePlatformCredentialCombinedWithSystemSerialNumbersMatchedChassis()
+            throws Exception {
+
+        DeviceInfoReport deviceInfoReport = buildReport(new HardwareInfo(
+                DeviceInfoEnums.NOT_SPECIFIED, DeviceInfoEnums.NOT_SPECIFIED,
+                DeviceInfoEnums.NOT_SPECIFIED, TEST_CHASSIS_SERIAL_NUMBER,
+                DeviceInfoEnums.NOT_SPECIFIED, DeviceInfoEnums.NOT_SPECIFIED));
+
+        byte[] certBytes = Files.readAllBytes(Paths.get(Objects.requireNonNull(CertificateTest.class.
+                getResource(INTEL_PLATFORM_CERT_2)).toURI()));
+
+        PlatformCredential pc = new PlatformCredential(certBytes);
+
+        EndorsementCredential ec = new EndorsementCredential(
+                Files.readAllBytes(Paths.get(Objects.requireNonNull(getClass().getResource(TEST_EK_CERT)).toURI())));
+
+        AppraisalStatus result =
+                CredentialValidator.validatePlatformCredentialAttributes(pc,
+                        deviceInfoReport, ec);
+        assertEquals(AppraisalStatus.Status.PASS, result.getAppStatus());
+        assertEquals(SupplyChainCredentialValidator.PLATFORM_ATTRIBUTES_VALID,
+                result.getMessage());
+    }
+
     /**
      * Checks if the Platform Credential validator appropriately fails
      * when there are no serial numbers returned from the device.