From 962ca45bb7b383452c0d0a312bc0443054ba6c7b Mon Sep 17 00:00:00 2001 From: chubtub <43381989+chubtub@users.noreply.github.com> Date: Thu, 21 Oct 2021 18:45:46 -0400 Subject: [PATCH 01/22] Modify ACA RIM validation to search for a signing cert if the base RIM does not have an embedded cert. Validate the ca chain of the found signing cert. --- .../SupplyChainValidationServiceImpl.java | 48 ++-- ...eferenceManifestDetailsPageController.java | 69 +++--- .../utils/ReferenceManifestValidator.java | 228 +++++++++++------- 3 files changed, 193 insertions(+), 152 deletions(-) diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/service/SupplyChainValidationServiceImpl.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/service/SupplyChainValidationServiceImpl.java index 88900294..9b15d04d 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/service/SupplyChainValidationServiceImpl.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/service/SupplyChainValidationServiceImpl.java @@ -46,15 +46,12 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Import; import org.springframework.stereotype.Service; -import java.io.ByteArrayInputStream; import java.io.IOException; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.cert.CertificateException; -import java.security.cert.X509Certificate; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; @@ -95,7 +92,8 @@ public class SupplyChainValidationServiceImpl implements SupplyChainValidationSe /** * Constructor to set just the CertificateManager, so that cert chain validating * methods can be called from outside classes. - * @param certificateManager the cert manager + * + * @param certificateManager the cert manager */ public SupplyChainValidationServiceImpl(final CertificateManager certificateManager) { this.certificateManager = certificateManager; @@ -135,6 +133,7 @@ public class SupplyChainValidationServiceImpl implements SupplyChainValidationSe /** * Allows other service access to the policy information. + * * @return supply chain policy */ public SupplyChainPolicy getPolicy() { @@ -243,7 +242,7 @@ public class SupplyChainValidationServiceImpl implements SupplyChainValidationSe } else { validations.add(new SupplyChainValidation(platformType, AppraisalStatus.Status.FAIL, new ArrayList<>(pcs), pcErrorMessage)); - } + } } } @@ -425,33 +424,20 @@ public class SupplyChainValidationServiceImpl implements SupplyChainValidationSe // verify signatures ReferenceManifestValidator referenceManifestValidator = - new ReferenceManifestValidator( - new ByteArrayInputStream(baseReferenceManifest.getRimBytes())); + new ReferenceManifestValidator(); + referenceManifestValidator.setRim(baseReferenceManifest); - - for (SwidResource swidRes : resources) { - supportReferenceManifest = SupportReferenceManifest.select(referenceManifestManager) - .byHexDecHash(swidRes.getHashValue()).getRIM(); - if (supportReferenceManifest != null - && swidRes.getName().equals(supportReferenceManifest.getFileName())) { - referenceManifestValidator.validateSupportRimHash( - supportReferenceManifest.getRimBytes(), swidRes.getHashValue()); - } else { - supportReferenceManifest = null; - } - } //Validate signing cert Set allCerts = CertificateAuthorityCredential.select(certificateManager).getCertificates(); CertificateAuthorityCredential signingCert = null; for (CertificateAuthorityCredential cert : allCerts) { - if (Arrays.equals(cert.getEncodedPublicKey(), - referenceManifestValidator.getPublicKey().getEncoded())) { - signingCert = cert; - KeyStore keyStore = getCaChain(signingCert); + signingCert = cert; + KeyStore keyStore = getCaChain(signingCert); + if (referenceManifestValidator.validateXmlSignature(signingCert)) { try { - X509Certificate x509Cert = signingCert.getX509Certificate(); - if (!SupplyChainCredentialValidator.verifyCertificate(x509Cert, keyStore)) { + if (!SupplyChainCredentialValidator.verifyCertificate( + signingCert.getX509Certificate(), keyStore)) { passed = false; fwStatus = new AppraisalStatus(FAIL, "Firmware validation failed: invalid certificate path."); @@ -467,6 +453,18 @@ public class SupplyChainValidationServiceImpl implements SupplyChainValidationSe } } + for (SwidResource swidRes : resources) { + supportReferenceManifest = SupportReferenceManifest.select(referenceManifestManager) + .byHexDecHash(swidRes.getHashValue()).getRIM(); + if (supportReferenceManifest != null + && swidRes.getName().equals(supportReferenceManifest.getFileName())) { + referenceManifestValidator.validateSupportRimHash( + supportReferenceManifest.getRimBytes(), swidRes.getHashValue()); + } else { + supportReferenceManifest = null; + } + } + if (signingCert == null) { passed = false; fwStatus = new AppraisalStatus(FAIL, diff --git a/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/ReferenceManifestDetailsPageController.java b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/ReferenceManifestDetailsPageController.java index 7fe9308f..11b921bd 100644 --- a/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/ReferenceManifestDetailsPageController.java +++ b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/ReferenceManifestDetailsPageController.java @@ -31,12 +31,10 @@ import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.servlet.ModelAndView; -import java.io.ByteArrayInputStream; import java.io.IOException; import java.security.KeyStore; import java.security.NoSuchAlgorithmException; import java.security.cert.CertificateException; -import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -68,9 +66,9 @@ public class ReferenceManifestDetailsPageController * Constructor providing the Page's display and routing specification. * * @param referenceManifestManager the reference manifest manager. - * @param referenceDigestManager the reference digest manager. - * @param referenceEventManager the reference event manager. - * @param certificateManager the certificate manager. + * @param referenceDigestManager the reference digest manager. + * @param referenceEventManager the reference event manager. + * @param certificateManager the certificate manager. */ @Autowired public ReferenceManifestDetailsPageController( @@ -144,19 +142,19 @@ public class ReferenceManifestDetailsPageController * * @param uuid database reference for the requested RIM. * @param referenceManifestManager the reference manifest manager. - * @param referenceDigestManager the reference digest manager. - * @param referenceEventManager the reference event manager. - * @param certificateManager the certificate manager. + * @param referenceDigestManager the reference digest manager. + * @param referenceEventManager the reference event manager. + * @param certificateManager the certificate manager. * @return mapping of the RIM information from the database. * @throws java.io.IOException error for reading file bytes. * @throws NoSuchAlgorithmException If an unknown Algorithm is encountered. * @throws CertificateException if a certificate doesn't parse. */ public static HashMap getRimDetailInfo(final UUID uuid, - final ReferenceManifestManager referenceManifestManager, - final ReferenceDigestManager referenceDigestManager, - final ReferenceEventManager referenceEventManager, - final CertificateManager certificateManager) throws IOException, + final ReferenceManifestManager referenceManifestManager, + final ReferenceDigestManager referenceDigestManager, + final ReferenceEventManager referenceEventManager, + final CertificateManager certificateManager) throws IOException, CertificateException, NoSuchAlgorithmException { HashMap data = new HashMap<>(); @@ -189,9 +187,9 @@ public class ReferenceManifestDetailsPageController * This method takes the place of an entire class for a string builder. * Gathers all information and returns it for displays. * - * @param baseRim established ReferenceManifest Type. + * @param baseRim established ReferenceManifest Type. * @param referenceManifestManager the reference manifest manager. - * @param certificateManager the certificate manager. + * @param certificateManager the certificate manager. * @return mapping of the RIM information from the database. * @throws java.io.IOException error for reading file bytes. * @throws NoSuchAlgorithmException If an unknown Algorithm is encountered. @@ -284,6 +282,7 @@ public class ReferenceManifestDetailsPageController } // going to have to pull the filename and grab that from the DB // to get the id to make the link + RIM_VALIDATOR.setRim(baseRim); for (SwidResource swidRes : resources) { if (support != null && swidRes.getHashValue() .equalsIgnoreCase(support.getHexDecHash())) { @@ -305,29 +304,25 @@ public class ReferenceManifestDetailsPageController data.put("pcrList", support.getExpectedPCRList()); } - RIM_VALIDATOR.validateXmlSignature(new ByteArrayInputStream(baseRim.getRimBytes())); +// RIM_VALIDATOR.validateXmlSignature(new ByteArrayInputStream(baseRim.getRimBytes())); Set certificates = CertificateAuthorityCredential.select(certificateManager) .getCertificates(); //Report invalid signature unless RIM_VALIDATOR validates it and cert path is valid data.put("signatureValid", false); - if (RIM_VALIDATOR.isSignatureValid()) { - for (CertificateAuthorityCredential cert : certificates) { - if (Arrays.equals(cert.getEncodedPublicKey(), - RIM_VALIDATOR.getPublicKey().getEncoded())) { - SupplyChainValidationServiceImpl scvsImpl = - new SupplyChainValidationServiceImpl(certificateManager); - KeyStore keystore = scvsImpl.getCaChain(cert); - X509Certificate signingCert = cert.getX509Certificate(); - try { - if (SupplyChainCredentialValidator.verifyCertificate(signingCert, - keystore)) { - data.replace("signatureValid", true); - } - } catch (SupplyChainValidatorException e) { - LOGGER.error("Error verifying cert chain: " + e.getMessage()); + for (CertificateAuthorityCredential cert : certificates) { + SupplyChainValidationServiceImpl scvsImpl = + new SupplyChainValidationServiceImpl(certificateManager); + KeyStore keystore = scvsImpl.getCaChain(cert); + if (RIM_VALIDATOR.validateXmlSignature(cert)) { + try { + if (SupplyChainCredentialValidator.verifyCertificate( + cert.getX509Certificate(), keystore)) { + data.replace("signatureValid", true); + break; } - break; + } catch (SupplyChainValidatorException e) { + LOGGER.error("Error verifying cert chain: " + e.getMessage()); } } } @@ -349,7 +344,7 @@ public class ReferenceManifestDetailsPageController * This method takes the place of an entire class for a string builder. * Gathers all information and returns it for displays. * - * @param support established ReferenceManifest Type. + * @param support established ReferenceManifest Type. * @param referenceManifestManager the reference manifest manager. * @return mapping of the RIM information from the database. * @throws java.io.IOException error for reading file bytes. @@ -414,8 +409,8 @@ public class ReferenceManifestDetailsPageController digestMap.put(tpe.getEventDigestStr(), tpe); if (!support.isSwidSupplemental() && !tpe.eventCompare( - measurementsProcess.getEventByNumber( - tpe.getEventNumber()))) { + measurementsProcess.getEventByNumber( + tpe.getEventNumber()))) { tpe.setError(true); } tpmPcrEvents.add(tpe); @@ -509,10 +504,10 @@ public class ReferenceManifestDetailsPageController * This method takes the place of an entire class for a string builder. * Gathers all information and returns it for displays. * - * @param measurements established ReferenceManifest Type. + * @param measurements established ReferenceManifest Type. * @param referenceManifestManager the reference manifest manager. - * @param referenceDigestManager the reference digest manager. - * @param referenceEventManager the reference event manager. + * @param referenceDigestManager the reference digest manager. + * @param referenceEventManager the reference event manager. * @return mapping of the RIM information from the database. * @throws java.io.IOException error for reading file bytes. * @throws NoSuchAlgorithmException If an unknown Algorithm is encountered. diff --git a/HIRS_Utils/src/main/java/hirs/utils/ReferenceManifestValidator.java b/HIRS_Utils/src/main/java/hirs/utils/ReferenceManifestValidator.java index 6bb931cf..daf0096a 100644 --- a/HIRS_Utils/src/main/java/hirs/utils/ReferenceManifestValidator.java +++ b/HIRS_Utils/src/main/java/hirs/utils/ReferenceManifestValidator.java @@ -1,7 +1,11 @@ package hirs.utils; +import hirs.data.persist.ReferenceManifest; +import hirs.data.persist.certificate.CertificateAuthorityCredential; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.bouncycastle.asn1.x509.Extension; +import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils; import org.w3c.dom.Document; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; @@ -23,7 +27,6 @@ import javax.xml.crypto.dsig.XMLSignatureException; import javax.xml.crypto.dsig.XMLSignatureFactory; import javax.xml.crypto.dsig.dom.DOMValidateContext; import javax.xml.crypto.dsig.keyinfo.KeyInfo; -import javax.xml.crypto.dsig.keyinfo.KeyValue; import javax.xml.crypto.dsig.keyinfo.X509Data; import javax.xml.transform.Source; import javax.xml.transform.Transformer; @@ -34,14 +37,17 @@ import javax.xml.transform.dom.DOMResult; import javax.xml.transform.stream.StreamSource; import javax.xml.validation.Schema; import javax.xml.validation.SchemaFactory; +import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.security.Key; -import java.security.KeyException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.PublicKey; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; +import java.util.Arrays; import java.util.Iterator; @@ -52,7 +58,7 @@ import java.util.Iterator; */ public class ReferenceManifestValidator { private static final String SIGNATURE_ALGORITHM_RSA_SHA256 = - "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"; + "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"; private static final String SCHEMA_PACKAGE = "hirs.utils.xjc"; private static final String SCHEMA_URL = "swid_schema.xsd"; private static final String SCHEMA_LANGUAGE = XMLConstants.W3C_XML_SCHEMA_NS_URI; @@ -63,12 +69,29 @@ public class ReferenceManifestValidator { private static final int RADIX = 16; private static final Logger LOGGER = LogManager.getLogger(ReferenceManifestValidator.class); + private Document rim; private Unmarshaller unmarshaller; private PublicKey publicKey; private Schema schema; private String subjectKeyIdentifier; private boolean signatureValid, supportRimValid; + /** + * Setter for the RIM to be validated. The ReferenceManifest object is converted into a + * Document for processing. + * + * @param rim ReferenceManifest object + */ + public void setRim(ReferenceManifest rim) { + try { + Document doc = validateSwidtagSchema(removeXMLWhitespace(new StreamSource( + new ByteArrayInputStream(rim.getRimBytes())))); + this.rim = doc; + } catch (IOException e) { + LOGGER.error("Error while unmarshalling rim bytes: " + e.getMessage()); + } + } + /** * Getter for signatureValid. * @@ -98,6 +121,7 @@ public class ReferenceManifestValidator { /** * Getter for subjectKeyIdentifier. + * * @return subjectKeyIdentifier */ public String getSubjectKeyIdentifier() { @@ -114,43 +138,16 @@ public class ReferenceManifestValidator { .getClassLoader().getResourceAsStream(SCHEMA_URL); SchemaFactory schemaFactory = SchemaFactory.newInstance(SCHEMA_LANGUAGE); schema = schemaFactory.newSchema(new StreamSource(is)); + rim = null; signatureValid = false; supportRimValid = false; publicKey = null; - subjectKeyIdentifier = ""; + subjectKeyIdentifier = "(not found)"; } catch (SAXException e) { LOGGER.warn("Error setting schema for validation!"); } } - /** - * This constructor is used for a quick signature check which bypasses the loading of - * the schema url into memory. As a result the full stream is not validated against the schema. - * - * @param input xml data byte array. - */ - public ReferenceManifestValidator(final InputStream input) { - try { - signatureValid = validateSignedXMLDocument( - removeXMLWhitespace(new StreamSource(input))); - } catch (IOException e) { - LOGGER.warn("Error during unmarshal: " + e.getMessage()); - } - } - - /** - * This method calculates the SHA256 hash of the input byte array and compares it against - * the value passed in. - * - * @param input byte array to hash. - * @param expected value to compare against. - */ - public void validateSupportRimHash(final byte[] input, final String expected) { - String calculatedHash = getHashValue(input, SHA256); - LOGGER.info("Calculated hash: " + calculatedHash + ", actual: " + expected); - supportRimValid = calculatedHash.equals(expected); - } - /** * This method validates the xml signature in the stream and stores the * result for public access. @@ -160,16 +157,67 @@ public class ReferenceManifestValidator { public void validateXmlSignature(final InputStream input) { try { Document doc = validateSwidtagSchema(removeXMLWhitespace(new StreamSource(input))); - signatureValid = validateSignedXMLDocument(doc); + //signatureValid = validateSignedXMLDocument(doc); } catch (IOException e) { LOGGER.warn("Error during unmarshal: " + e.getMessage()); } } + public boolean validateXmlSignature(final CertificateAuthorityCredential cert) { + DOMValidateContext context = null; + try { + NodeList nodes = rim.getElementsByTagNameNS(XMLSignature.XMLNS, "Signature"); + if (nodes.getLength() == 0) { + LOGGER.error("Cannot validate RIM, signature element not found!"); + return false; + } + NodeList certElement = rim.getElementsByTagName("X509Certificate"); + if (certElement.getLength() > 0) { + X509Certificate embeddedCert = parseCertFromPEMString( + certElement.item(0).getTextContent()); + subjectKeyIdentifier = getCertificateSubjectKeyIdentifier(embeddedCert); + if (Arrays.equals(embeddedCert.getPublicKey().getEncoded(), + cert.getEncodedPublicKey())) { + context = new DOMValidateContext(new X509KeySelector(), nodes.item(0)); + } + } else { + subjectKeyIdentifier = getKeyName(rim); + if (subjectKeyIdentifier.equals(cert.getSubjectKeyIdString())) { + context = new DOMValidateContext(cert.getX509Certificate().getPublicKey(), + nodes.item(0)); + } + } + if (context != null) { + publicKey = cert.getX509Certificate().getPublicKey(); + return validateSignedXMLDocument(context); + } + } catch (CertificateException e) { + LOGGER.warn("Error parsing embedded certificate from RIM: " + e.getMessage()); + } catch (IOException e) { + LOGGER.warn("Error while parsing certificate data: " + e.getMessage()); + } + + return false; + } + + /** + * This method calculates the SHA256 hash of the input byte array and compares it against + * the value passed in. + * + * @param input byte array to hash. + * @param expected value to compare against. + */ + public void validateSupportRimHash(final byte[] input, final String expected) { + String calculatedHash = getHashValue(input, SHA256); + LOGGER.info("Calculated hash: " + calculatedHash + ", actual: " + expected); + supportRimValid = calculatedHash.equals(expected); + } + /** * This method calculates the digest of a byte array based on the hashing algorithm passed in. + * * @param input byte array. - * @param sha hash algorithm. + * @param sha hash algorithm. * @return String digest. */ private String getHashValue(final byte[] input, final String sha) { @@ -181,7 +229,7 @@ public class ReferenceManifestValidator { for (int i = 0; i < bytes.length; i++) { sb.append(Integer.toString((bytes[i] & EIGHT_BIT_MASK) - + LEFT_SHIFT, RADIX).substring(1)); + + LEFT_SHIFT, RADIX).substring(1)); } resultString = sb.toString(); } catch (NoSuchAlgorithmException grex) { @@ -191,37 +239,18 @@ public class ReferenceManifestValidator { return resultString; } - /** - * This method validates a Document with a signature element. - * - * @param doc - */ - private boolean validateSignedXMLDocument(final Document doc) { - DOMValidateContext context; - boolean isValid = false; + private boolean validateSignedXMLDocument(final DOMValidateContext context) { try { - NodeList nodes = doc.getElementsByTagNameNS(XMLSignature.XMLNS, "Signature"); - if (nodes.getLength() == 0) { - throw new Exception("Signature element not found!"); - } - X509KeySelector keySelector = new ReferenceManifestValidator.X509KeySelector(); - context = new DOMValidateContext(keySelector, nodes.item(0)); XMLSignatureFactory sigFactory = XMLSignatureFactory.getInstance("DOM"); XMLSignature signature = sigFactory.unmarshalXMLSignature(context); - isValid = signature.validate(context); - publicKey = keySelector.getPublicKey(); - subjectKeyIdentifier = getKeyName(doc); + return signature.validate(context); } catch (MarshalException e) { LOGGER.warn("Error while unmarshalling XML signature: " + e.getMessage()); } catch (XMLSignatureException e) { LOGGER.warn("Error while validating XML signature: " + e.getMessage()); - } catch (KeySelectorException e) { - LOGGER.warn("Public key not found in XML signature: " + e.getMessage()); - } catch (Exception e) { - LOGGER.warn(e.getMessage()); } - return isValid; + return false; } /** @@ -230,8 +259,6 @@ public class ReferenceManifestValidator { * an XML signature. */ public static class X509KeySelector extends KeySelector { - private PublicKey publicKey; - /** * This method selects a public key for validation. * PKs are parsed preferentially from the following elements: @@ -240,10 +267,10 @@ public class ReferenceManifestValidator { * The parsed PK is then verified based on the provided algorithm before * being returned in a KeySelectorResult. * - * @param keyinfo object containing the cert. - * @param purpose purpose. + * @param keyinfo object containing the cert. + * @param purpose purpose. * @param algorithm algorithm. - * @param context XMLCryptoContext. + * @param context XMLCryptoContext. * @return KeySelectorResult holding the PublicKey. * @throws KeySelectorException exception. */ @@ -251,7 +278,7 @@ public class ReferenceManifestValidator { final KeySelector.Purpose purpose, final AlgorithmMethod algorithm, final XMLCryptoContext context) - throws KeySelectorException { + throws KeySelectorException { Iterator keyinfoItr = keyinfo.getContent().iterator(); while (keyinfoItr.hasNext()) { XMLStructure element = (XMLStructure) keyinfoItr.next(); @@ -261,32 +288,22 @@ public class ReferenceManifestValidator { while (dataItr.hasNext()) { Object object = dataItr.next(); if (object instanceof X509Certificate) { - publicKey = ((X509Certificate) object).getPublicKey(); - break; + final PublicKey publicKey = ((X509Certificate) object).getPublicKey(); + if (areAlgorithmsEqual(algorithm.getAlgorithm(), publicKey.getAlgorithm())) { + return new ReferenceManifestValidator.X509KeySelector + .RIMKeySelectorResult(publicKey); + } } } - } else if (element instanceof KeyValue) { - try { - publicKey = ((KeyValue) element).getPublicKey(); - } catch (KeyException e) { - LOGGER.warn("KeyException thrown while getting PK from KeyValue: " - + e.getMessage()); - } } } - if (areAlgorithmsEqual(algorithm.getAlgorithm(), - publicKey.getAlgorithm())) { - return new ReferenceManifestValidator.X509KeySelector - .RIMKeySelectorResult(publicKey); - } - throw new KeySelectorException("No key found!"); } /** * This method checks if two strings refer to the same algorithm. * - * @param uri string 1 + * @param uri string 1 * @param name string 2 * @return true if equal, false if not */ @@ -294,19 +311,10 @@ public class ReferenceManifestValidator { return uri.equals(SIGNATURE_ALGORITHM_RSA_SHA256) && name.equalsIgnoreCase("RSA"); } - /** - * Getter for the public key that is parsed in the select() method above. - * - * @return PublicKey encoded in the X509 cert. - */ - public PublicKey getPublicKey() { - return publicKey; - } - /** * This internal class creates a KeySelectorResult from the public key. */ - private static class RIMKeySelectorResult implements KeySelectorResult { + private class RIMKeySelectorResult implements KeySelectorResult { private Key key; RIMKeySelectorResult(final Key key) { @@ -319,6 +327,46 @@ public class ReferenceManifestValidator { } } + /** + * This method extracts certificate bytes from a string. The bytes are assumed to be + * PEM format, and a header and footer are concatenated with the input string to + * facilitate proper parsing. + * + * @param pemString the input string + * @return an X509Certificate created from the string + * @throws CertificateException if instantiating the CertificateFactory errors + */ + public X509Certificate parseCertFromPEMString(String pemString) throws CertificateException { + String CERTIFICATE_HEADER = "-----BEGIN CERTIFICATE-----"; + String CERTIFICATE_FOOTER = "-----END CERTIFICATE-----"; + try { + CertificateFactory factory = CertificateFactory.getInstance("X.509"); + InputStream inputStream = new ByteArrayInputStream((CERTIFICATE_HEADER + + System.lineSeparator() + + pemString + + System.lineSeparator() + + CERTIFICATE_FOOTER).getBytes()); + return (X509Certificate) factory.generateCertificate(inputStream); + } catch (CertificateException e) { + throw e; + } + } + + /** + * This method returns the subjectKeyIdentifier from a given X509Certificate. + * @param certificate the cert to pull the subjectKeyIdentifier from + * @return the String representation of the subjectKeyIdentifier + * @throws IOException + */ + private String getCertificateSubjectKeyIdentifier(X509Certificate certificate) throws IOException { + String decodedValue = null; + byte[] extension = certificate.getExtensionValue(Extension.subjectKeyIdentifier.getId()); + if (extension != null && extension.length > 0) { + decodedValue = JcaX509ExtensionUtils.parseExtensionValue(extension).toString(); + } + return decodedValue.substring(1);//Drop the # at the beginning of the string + } + /** * This method parses the subject key identifier from the KeyName element of a signature. * @@ -368,7 +416,7 @@ public class ReferenceManifestValidator { TransformerFactory tf = TransformerFactory.newInstance(); Source identitySource = new StreamSource( ReferenceManifestValidator.class.getClassLoader() - .getResourceAsStream(IDENTITY_TRANSFORM)); + .getResourceAsStream(IDENTITY_TRANSFORM)); Document doc = null; try { Transformer transformer = tf.newTransformer(identitySource); From bc7e07583f1264e5fdb94ab133603e5fc0b21f31 Mon Sep 17 00:00:00 2001 From: chubtub <43381989+chubtub@users.noreply.github.com> Date: Wed, 27 Oct 2021 13:41:55 -0400 Subject: [PATCH 02/22] Match only the actual extension bytes of the SKID --- .../src/main/java/hirs/utils/ReferenceManifestValidator.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/HIRS_Utils/src/main/java/hirs/utils/ReferenceManifestValidator.java b/HIRS_Utils/src/main/java/hirs/utils/ReferenceManifestValidator.java index daf0096a..213b3326 100644 --- a/HIRS_Utils/src/main/java/hirs/utils/ReferenceManifestValidator.java +++ b/HIRS_Utils/src/main/java/hirs/utils/ReferenceManifestValidator.java @@ -182,7 +182,7 @@ public class ReferenceManifestValidator { } } else { subjectKeyIdentifier = getKeyName(rim); - if (subjectKeyIdentifier.equals(cert.getSubjectKeyIdString())) { + if (subjectKeyIdentifier.equals(cert.getSubjectKeyIdString().substring(8))) { context = new DOMValidateContext(cert.getX509Certificate().getPublicKey(), nodes.item(0)); } @@ -354,6 +354,7 @@ public class ReferenceManifestValidator { /** * This method returns the subjectKeyIdentifier from a given X509Certificate. + * * @param certificate the cert to pull the subjectKeyIdentifier from * @return the String representation of the subjectKeyIdentifier * @throws IOException From 3a6be133eb7a13fa86ee182576f824b4808f4a65 Mon Sep 17 00:00:00 2001 From: chubtub <43381989+chubtub@users.noreply.github.com> Date: Thu, 28 Oct 2021 16:13:36 -0400 Subject: [PATCH 03/22] Checkstyle changes --- ...eferenceManifestDetailsPageController.java | 9 +-- .../utils/ReferenceManifestValidator.java | 70 ++++++++++--------- 2 files changed, 43 insertions(+), 36 deletions(-) diff --git a/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/ReferenceManifestDetailsPageController.java b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/ReferenceManifestDetailsPageController.java index 11b921bd..309bab5f 100644 --- a/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/ReferenceManifestDetailsPageController.java +++ b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/ReferenceManifestDetailsPageController.java @@ -151,10 +151,11 @@ public class ReferenceManifestDetailsPageController * @throws CertificateException if a certificate doesn't parse. */ public static HashMap getRimDetailInfo(final UUID uuid, - final ReferenceManifestManager referenceManifestManager, - final ReferenceDigestManager referenceDigestManager, - final ReferenceEventManager referenceEventManager, - final CertificateManager certificateManager) throws IOException, + final ReferenceManifestManager referenceManifestManager, + final ReferenceDigestManager referenceDigestManager, + final ReferenceEventManager referenceEventManager, + final CertificateManager certificateManager) + throws IOException, CertificateException, NoSuchAlgorithmException { HashMap data = new HashMap<>(); diff --git a/HIRS_Utils/src/main/java/hirs/utils/ReferenceManifestValidator.java b/HIRS_Utils/src/main/java/hirs/utils/ReferenceManifestValidator.java index 213b3326..dff855ba 100644 --- a/HIRS_Utils/src/main/java/hirs/utils/ReferenceManifestValidator.java +++ b/HIRS_Utils/src/main/java/hirs/utils/ReferenceManifestValidator.java @@ -40,6 +40,7 @@ import javax.xml.validation.SchemaFactory; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; +import java.io.UnsupportedEncodingException; import java.security.Key; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; @@ -82,7 +83,7 @@ public class ReferenceManifestValidator { * * @param rim ReferenceManifest object */ - public void setRim(ReferenceManifest rim) { + public void setRim(final ReferenceManifest rim) { try { Document doc = validateSwidtagSchema(removeXMLWhitespace(new StreamSource( new ByteArrayInputStream(rim.getRimBytes())))); @@ -149,20 +150,15 @@ public class ReferenceManifestValidator { } /** - * This method validates the xml signature in the stream and stores the - * result for public access. + * This method attempts to validate the signature element of the instance's RIM + * using a given cert. The cert is compared to either the RIM's embedded certificate + * or the RIM's subject key identifier. If the cert is matched then validation proceeds, + * otherwise validation ends. * - * @param input the xml data stream. + * @param cert the cert to be checked against the RIM + * @return true if the signature element is validated, false otherwise */ - public void validateXmlSignature(final InputStream input) { - try { - Document doc = validateSwidtagSchema(removeXMLWhitespace(new StreamSource(input))); - //signatureValid = validateSignedXMLDocument(doc); - } catch (IOException e) { - LOGGER.warn("Error during unmarshal: " + e.getMessage()); - } - } - + @SuppressWarnings("magicnumber") public boolean validateXmlSignature(final CertificateAuthorityCredential cert) { DOMValidateContext context = null; try { @@ -175,10 +171,12 @@ public class ReferenceManifestValidator { if (certElement.getLength() > 0) { X509Certificate embeddedCert = parseCertFromPEMString( certElement.item(0).getTextContent()); - subjectKeyIdentifier = getCertificateSubjectKeyIdentifier(embeddedCert); - if (Arrays.equals(embeddedCert.getPublicKey().getEncoded(), - cert.getEncodedPublicKey())) { - context = new DOMValidateContext(new X509KeySelector(), nodes.item(0)); + if (embeddedCert != null) { + subjectKeyIdentifier = getCertificateSubjectKeyIdentifier(embeddedCert); + if (Arrays.equals(embeddedCert.getPublicKey().getEncoded(), + cert.getEncodedPublicKey())) { + context = new DOMValidateContext(new X509KeySelector(), nodes.item(0)); + } } } else { subjectKeyIdentifier = getKeyName(rim); @@ -191,10 +189,10 @@ public class ReferenceManifestValidator { publicKey = cert.getX509Certificate().getPublicKey(); return validateSignedXMLDocument(context); } - } catch (CertificateException e) { - LOGGER.warn("Error parsing embedded certificate from RIM: " + e.getMessage()); } catch (IOException e) { LOGGER.warn("Error while parsing certificate data: " + e.getMessage()); + } catch (Exception e) { + e.printStackTrace(); } return false; @@ -289,7 +287,8 @@ public class ReferenceManifestValidator { Object object = dataItr.next(); if (object instanceof X509Certificate) { final PublicKey publicKey = ((X509Certificate) object).getPublicKey(); - if (areAlgorithmsEqual(algorithm.getAlgorithm(), publicKey.getAlgorithm())) { + if (areAlgorithmsEqual(algorithm.getAlgorithm(), + publicKey.getAlgorithm())) { return new ReferenceManifestValidator.X509KeySelector .RIMKeySelectorResult(publicKey); } @@ -314,7 +313,7 @@ public class ReferenceManifestValidator { /** * This internal class creates a KeySelectorResult from the public key. */ - private class RIMKeySelectorResult implements KeySelectorResult { + private static class RIMKeySelectorResult implements KeySelectorResult { private Key key; RIMKeySelectorResult(final Key key) { @@ -333,22 +332,26 @@ public class ReferenceManifestValidator { * facilitate proper parsing. * * @param pemString the input string - * @return an X509Certificate created from the string - * @throws CertificateException if instantiating the CertificateFactory errors + * @return an X509Certificate created from the string, or null + * @throws Exception if certificate cannot be successfully parsed */ - public X509Certificate parseCertFromPEMString(String pemString) throws CertificateException { - String CERTIFICATE_HEADER = "-----BEGIN CERTIFICATE-----"; - String CERTIFICATE_FOOTER = "-----END CERTIFICATE-----"; + public X509Certificate parseCertFromPEMString(final String pemString) throws Exception { + String certificateHeader = "-----BEGIN CERTIFICATE-----"; + String certificateFooter = "-----END CERTIFICATE-----"; try { CertificateFactory factory = CertificateFactory.getInstance("X.509"); - InputStream inputStream = new ByteArrayInputStream((CERTIFICATE_HEADER + InputStream inputStream = new ByteArrayInputStream((certificateHeader + System.lineSeparator() + pemString + System.lineSeparator() - + CERTIFICATE_FOOTER).getBytes()); + + certificateFooter).getBytes("UTF-8")); return (X509Certificate) factory.generateCertificate(inputStream); } catch (CertificateException e) { - throw e; + LOGGER.warn("Error creating CertificateFactory instance: " + e.getMessage()); + } catch (UnsupportedEncodingException e) { + LOGGER.warn("Error while parsing cert from PEM string: " + e.getMessage()); + } finally { + throw new Exception("Error parsing certificate from PEM string!"); } } @@ -359,13 +362,16 @@ public class ReferenceManifestValidator { * @return the String representation of the subjectKeyIdentifier * @throws IOException */ - private String getCertificateSubjectKeyIdentifier(X509Certificate certificate) throws IOException { - String decodedValue = null; + private String getCertificateSubjectKeyIdentifier(final X509Certificate certificate) + throws IOException { + String decodedValue; byte[] extension = certificate.getExtensionValue(Extension.subjectKeyIdentifier.getId()); if (extension != null && extension.length > 0) { decodedValue = JcaX509ExtensionUtils.parseExtensionValue(extension).toString(); + } else { + decodedValue = " "; //Unlikely that a proper X509Certificate does not have a skid } - return decodedValue.substring(1);//Drop the # at the beginning of the string + return decodedValue.substring(1); //Drop the # at the beginning of the string } /** From 0c233ae7711845ae375d27d73f3b53f43f251f9b Mon Sep 17 00:00:00 2001 From: chubtub <43381989+chubtub@users.noreply.github.com> Date: Tue, 2 Nov 2021 12:10:55 -0400 Subject: [PATCH 04/22] Set signature validity so that the ACA can report accurately --- .../ReferenceManifestDetailsPageController.java | 1 - .../java/hirs/utils/ReferenceManifestValidator.java | 13 ++++++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/ReferenceManifestDetailsPageController.java b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/ReferenceManifestDetailsPageController.java index 309bab5f..4b740dd6 100644 --- a/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/ReferenceManifestDetailsPageController.java +++ b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/page/controllers/ReferenceManifestDetailsPageController.java @@ -305,7 +305,6 @@ public class ReferenceManifestDetailsPageController data.put("pcrList", support.getExpectedPCRList()); } -// RIM_VALIDATOR.validateXmlSignature(new ByteArrayInputStream(baseRim.getRimBytes())); Set certificates = CertificateAuthorityCredential.select(certificateManager) .getCertificates(); diff --git a/HIRS_Utils/src/main/java/hirs/utils/ReferenceManifestValidator.java b/HIRS_Utils/src/main/java/hirs/utils/ReferenceManifestValidator.java index dff855ba..81e26913 100644 --- a/HIRS_Utils/src/main/java/hirs/utils/ReferenceManifestValidator.java +++ b/HIRS_Utils/src/main/java/hirs/utils/ReferenceManifestValidator.java @@ -187,7 +187,8 @@ public class ReferenceManifestValidator { } if (context != null) { publicKey = cert.getX509Certificate().getPublicKey(); - return validateSignedXMLDocument(context); + signatureValid = validateSignedXMLDocument(context); + return signatureValid; } } catch (IOException e) { LOGGER.warn("Error while parsing certificate data: " + e.getMessage()); @@ -207,8 +208,10 @@ public class ReferenceManifestValidator { */ public void validateSupportRimHash(final byte[] input, final String expected) { String calculatedHash = getHashValue(input, SHA256); - LOGGER.info("Calculated hash: " + calculatedHash + ", actual: " + expected); supportRimValid = calculatedHash.equals(expected); + if (!supportRimValid) { + LOGGER.info("Unmatched support RIM hash! Expected: " + expected + ", actual: " + calculatedHash); + } } /** @@ -335,7 +338,7 @@ public class ReferenceManifestValidator { * @return an X509Certificate created from the string, or null * @throws Exception if certificate cannot be successfully parsed */ - public X509Certificate parseCertFromPEMString(final String pemString) throws Exception { + private X509Certificate parseCertFromPEMString(final String pemString) throws Exception { String certificateHeader = "-----BEGIN CERTIFICATE-----"; String certificateFooter = "-----END CERTIFICATE-----"; try { @@ -350,9 +353,9 @@ public class ReferenceManifestValidator { LOGGER.warn("Error creating CertificateFactory instance: " + e.getMessage()); } catch (UnsupportedEncodingException e) { LOGGER.warn("Error while parsing cert from PEM string: " + e.getMessage()); - } finally { - throw new Exception("Error parsing certificate from PEM string!"); } + + return null; } /** From e0a1e53d93485d028ba613f7574c9659d09bea91 Mon Sep 17 00:00:00 2001 From: chubtub <43381989+chubtub@users.noreply.github.com> Date: Mon, 8 Nov 2021 14:26:03 -0500 Subject: [PATCH 05/22] Handle an exception thrown when the provisioner fails to send RIM files to the ACA --- .../service/SupplyChainValidationServiceImpl.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/service/SupplyChainValidationServiceImpl.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/service/SupplyChainValidationServiceImpl.java index 9b15d04d..cf72835a 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/service/SupplyChainValidationServiceImpl.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/service/SupplyChainValidationServiceImpl.java @@ -580,11 +580,11 @@ public class SupplyChainValidationServiceImpl implements SupplyChainValidationSe } else { fwStatus = new AppraisalStatus(FAIL, String.format("Firmware Validation failed: " + "%s for %s can not be found", failedString, manufacturer)); + EventLogMeasurements eventLog = (EventLogMeasurements) measurement; + eventLog.setOverallValidationResult(fwStatus.getAppStatus()); + this.referenceManifestManager.update(eventLog); } - EventLogMeasurements eventLog = (EventLogMeasurements) measurement; - eventLog.setOverallValidationResult(fwStatus.getAppStatus()); - this.referenceManifestManager.update(eventLog); return buildValidationRecord(SupplyChainValidation.ValidationType.FIRMWARE, fwStatus.getAppStatus(), fwStatus.getMessage(), validationObject, level); } From c944aab335f9963589a991e5567669db9b63450c Mon Sep 17 00:00:00 2001 From: Cyrus <24922493+cyrus-dev@users.noreply.github.com> Date: Fri, 12 Nov 2021 11:31:00 -0500 Subject: [PATCH 06/22] Added a log statement to get more information about the certificate that is failing signature verification. --- .../java/hirs/validation/SupplyChainCredentialValidator.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/HIRS_Utils/src/main/java/hirs/validation/SupplyChainCredentialValidator.java b/HIRS_Utils/src/main/java/hirs/validation/SupplyChainCredentialValidator.java index 752cca41..3972e025 100644 --- a/HIRS_Utils/src/main/java/hirs/validation/SupplyChainCredentialValidator.java +++ b/HIRS_Utils/src/main/java/hirs/validation/SupplyChainCredentialValidator.java @@ -1632,6 +1632,8 @@ public final class SupplyChainCredentialValidator implements CredentialValidator } catch (InvalidKeyException | CertificateException | NoSuchAlgorithmException | NoSuchProviderException | SignatureException e) { LOGGER.error("Exception thrown while verifying certificate", e); + LOGGER.error(String.format("%s.verify(%s)", cert.getSubjectDN(), + signingCert.getSubjectDN())); return false; } @@ -1678,6 +1680,8 @@ public final class SupplyChainCredentialValidator implements CredentialValidator return cert.isSignatureValid(contentVerifierProvider); } catch (OperatorCreationException | CertException e) { LOGGER.error("Exception thrown while verifying certificate", e); + LOGGER.error(String.format("%s.isSignatureValid(%s)", cert.getSerialNumber(), + signingKey.getFormat())); return false; } } From d31b710824f97b9d0405dcd3051cf4a904a0a81b Mon Sep 17 00:00:00 2001 From: Cyrus <24922493+cyrus-dev@users.noreply.github.com> Date: Fri, 12 Nov 2021 14:24:33 -0500 Subject: [PATCH 07/22] This is a test run to ignore endorsement credential verification during firmware testing. --- .../SupplyChainValidationServiceImpl.java | 43 +++++++++---------- .../utils/ReferenceManifestValidator.java | 3 +- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/service/SupplyChainValidationServiceImpl.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/service/SupplyChainValidationServiceImpl.java index 913f6e00..f2c4fd66 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/service/SupplyChainValidationServiceImpl.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/service/SupplyChainValidationServiceImpl.java @@ -37,7 +37,6 @@ import hirs.utils.BouncyCastleUtils; import hirs.utils.ReferenceManifestValidator; import hirs.validation.CredentialValidator; import hirs.validation.SupplyChainCredentialValidator; -import hirs.validation.SupplyChainValidatorException; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -431,27 +430,27 @@ public class SupplyChainValidationServiceImpl implements SupplyChainValidationSe Set allCerts = CertificateAuthorityCredential.select(certificateManager).getCertificates(); CertificateAuthorityCredential signingCert = null; - for (CertificateAuthorityCredential cert : allCerts) { - signingCert = cert; - KeyStore keyStore = getCaChain(signingCert); - if (referenceManifestValidator.validateXmlSignature(signingCert)) { - try { - if (!SupplyChainCredentialValidator.verifyCertificate( - signingCert.getX509Certificate(), keyStore)) { - passed = false; - fwStatus = new AppraisalStatus(FAIL, - "Firmware validation failed: invalid certificate path."); - } - } catch (IOException e) { - LOGGER.error("Error getting X509 cert from manager: " + e.getMessage()); - } catch (SupplyChainValidatorException e) { - LOGGER.error("Error validating cert against keystore: " + e.getMessage()); - fwStatus = new AppraisalStatus(FAIL, - "Firmware validation failed: invalid certificate path."); - } - break; - } - } +// for (CertificateAuthorityCredential cert : allCerts) {TODO: undo this +// signingCert = cert; +// KeyStore keyStore = getCaChain(signingCert); +// if (referenceManifestValidator.validateXmlSignature(signingCert)) { +// try { +//// if (!SupplyChainCredentialValidator.verifyCertificate( +//// signingCert.getX509Certificate(), keyStore)) { +// passed = false; +// fwStatus = new AppraisalStatus(FAIL, +// "Firmware validation failed: invalid certificate path."); +// } +// } catch (IOException e) { +// LOGGER.error("Error getting X509 cert from manager: " + e.getMessage()); +// } catch (SupplyChainValidatorException e) { +// LOGGER.error("Error validating cert against keystore: " + e.getMessage()); +// fwStatus = new AppraisalStatus(FAIL, +// "Firmware validation failed: invalid certificate path."); +// } +// break; +// } +// } for (SwidResource swidRes : resources) { supportReferenceManifest = SupportReferenceManifest.select(referenceManifestManager) diff --git a/HIRS_Utils/src/main/java/hirs/utils/ReferenceManifestValidator.java b/HIRS_Utils/src/main/java/hirs/utils/ReferenceManifestValidator.java index 81e26913..ba00415d 100644 --- a/HIRS_Utils/src/main/java/hirs/utils/ReferenceManifestValidator.java +++ b/HIRS_Utils/src/main/java/hirs/utils/ReferenceManifestValidator.java @@ -210,7 +210,8 @@ public class ReferenceManifestValidator { String calculatedHash = getHashValue(input, SHA256); supportRimValid = calculatedHash.equals(expected); if (!supportRimValid) { - LOGGER.info("Unmatched support RIM hash! Expected: " + expected + ", actual: " + calculatedHash); + LOGGER.info("Unmatched support RIM hash! Expected: " + expected + + ", actual: " + calculatedHash); } } From cfb9cb75353d0f79a942a2b1c8f0763a47946fb3 Mon Sep 17 00:00:00 2001 From: Cyrus <24922493+cyrus-dev@users.noreply.github.com> Date: Mon, 15 Nov 2021 09:43:31 -0500 Subject: [PATCH 08/22] Uncommented the code that deals with the signing certificate. --- .../SupplyChainValidationServiceImpl.java | 43 ++++++++++--------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/service/SupplyChainValidationServiceImpl.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/service/SupplyChainValidationServiceImpl.java index f2c4fd66..913f6e00 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/service/SupplyChainValidationServiceImpl.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/service/SupplyChainValidationServiceImpl.java @@ -37,6 +37,7 @@ import hirs.utils.BouncyCastleUtils; import hirs.utils.ReferenceManifestValidator; import hirs.validation.CredentialValidator; import hirs.validation.SupplyChainCredentialValidator; +import hirs.validation.SupplyChainValidatorException; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -430,27 +431,27 @@ public class SupplyChainValidationServiceImpl implements SupplyChainValidationSe Set allCerts = CertificateAuthorityCredential.select(certificateManager).getCertificates(); CertificateAuthorityCredential signingCert = null; -// for (CertificateAuthorityCredential cert : allCerts) {TODO: undo this -// signingCert = cert; -// KeyStore keyStore = getCaChain(signingCert); -// if (referenceManifestValidator.validateXmlSignature(signingCert)) { -// try { -//// if (!SupplyChainCredentialValidator.verifyCertificate( -//// signingCert.getX509Certificate(), keyStore)) { -// passed = false; -// fwStatus = new AppraisalStatus(FAIL, -// "Firmware validation failed: invalid certificate path."); -// } -// } catch (IOException e) { -// LOGGER.error("Error getting X509 cert from manager: " + e.getMessage()); -// } catch (SupplyChainValidatorException e) { -// LOGGER.error("Error validating cert against keystore: " + e.getMessage()); -// fwStatus = new AppraisalStatus(FAIL, -// "Firmware validation failed: invalid certificate path."); -// } -// break; -// } -// } + for (CertificateAuthorityCredential cert : allCerts) { + signingCert = cert; + KeyStore keyStore = getCaChain(signingCert); + if (referenceManifestValidator.validateXmlSignature(signingCert)) { + try { + if (!SupplyChainCredentialValidator.verifyCertificate( + signingCert.getX509Certificate(), keyStore)) { + passed = false; + fwStatus = new AppraisalStatus(FAIL, + "Firmware validation failed: invalid certificate path."); + } + } catch (IOException e) { + LOGGER.error("Error getting X509 cert from manager: " + e.getMessage()); + } catch (SupplyChainValidatorException e) { + LOGGER.error("Error validating cert against keystore: " + e.getMessage()); + fwStatus = new AppraisalStatus(FAIL, + "Firmware validation failed: invalid certificate path."); + } + break; + } + } for (SwidResource swidRes : resources) { supportReferenceManifest = SupportReferenceManifest.select(referenceManifestManager) From d5fcd06902f3ae211cf2eb41b1d43e8f66efb00d Mon Sep 17 00:00:00 2001 From: chubtub <43381989+chubtub@users.noreply.github.com> Date: Tue, 16 Nov 2021 16:59:56 -0500 Subject: [PATCH 09/22] Add error handling to clarify endorsement validation error --- .../SupplyChainCredentialValidator.java | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/HIRS_Utils/src/main/java/hirs/validation/SupplyChainCredentialValidator.java b/HIRS_Utils/src/main/java/hirs/validation/SupplyChainCredentialValidator.java index 752cca41..cc1d5cac 100644 --- a/HIRS_Utils/src/main/java/hirs/validation/SupplyChainCredentialValidator.java +++ b/HIRS_Utils/src/main/java/hirs/validation/SupplyChainCredentialValidator.java @@ -1629,11 +1629,18 @@ public final class SupplyChainCredentialValidator implements CredentialValidator try { cert.verify(signingCert.getPublicKey(), BouncyCastleProvider.PROVIDER_NAME); return true; - } catch (InvalidKeyException | CertificateException | NoSuchAlgorithmException - | NoSuchProviderException | SignatureException e) { - LOGGER.error("Exception thrown while verifying certificate", e); - return false; + } catch (InvalidKeyException e) { + LOGGER.warn("Incorrect key given to validate this cert's signature"); + } catch (CertificateException e) { + LOGGER.warn("Encoding error while validating this cert's signature"); + } catch (NoSuchAlgorithmException e) { + LOGGER.warn("Unsupported signature algorithm found during validation"); + } catch (NoSuchProviderException e) { + LOGGER.warn("Incorrect provider for cert signature validation"); + } catch (SignatureException e) { + LOGGER.warn("Exception thrown while verifying certificate", e); } + return false; } From 8b5027de4c10ff4c88b76d928c548cc507b6e5b6 Mon Sep 17 00:00:00 2001 From: chubtub <43381989+chubtub@users.noreply.github.com> Date: Wed, 17 Nov 2021 10:15:31 -0500 Subject: [PATCH 10/22] Modify log messages so that validation failures from certs in the keystore that are not actually in the chain do not raise undue attention --- .../validation/SupplyChainCredentialValidator.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/HIRS_Utils/src/main/java/hirs/validation/SupplyChainCredentialValidator.java b/HIRS_Utils/src/main/java/hirs/validation/SupplyChainCredentialValidator.java index ce67ea39..d86162a5 100644 --- a/HIRS_Utils/src/main/java/hirs/validation/SupplyChainCredentialValidator.java +++ b/HIRS_Utils/src/main/java/hirs/validation/SupplyChainCredentialValidator.java @@ -1630,15 +1630,15 @@ public final class SupplyChainCredentialValidator implements CredentialValidator cert.verify(signingCert.getPublicKey(), BouncyCastleProvider.PROVIDER_NAME); return true; } catch (InvalidKeyException e) { - LOGGER.warn("Incorrect key given to validate this cert's signature"); + LOGGER.info("Incorrect key given to validate this cert's signature"); } catch (CertificateException e) { - LOGGER.warn("Encoding error while validating this cert's signature"); + LOGGER.info("Encoding error while validating this cert's signature"); } catch (NoSuchAlgorithmException e) { - LOGGER.warn("Unsupported signature algorithm found during validation"); + LOGGER.info("Unsupported signature algorithm found during validation"); } catch (NoSuchProviderException e) { - LOGGER.warn("Incorrect provider for cert signature validation"); + LOGGER.info("Incorrect provider for cert signature validation"); } catch (SignatureException e) { - LOGGER.warn(String.format("%s.verify(%s)", cert.getSubjectDN(), + LOGGER.info(String.format("%s.verify(%s)", cert.getSubjectDN(), signingCert.getSubjectDN())); } return false; @@ -1685,8 +1685,8 @@ public final class SupplyChainCredentialValidator implements CredentialValidator new JcaContentVerifierProviderBuilder().setProvider("BC").build(signingKey); return cert.isSignatureValid(contentVerifierProvider); } catch (OperatorCreationException | CertException e) { - LOGGER.error("Exception thrown while verifying certificate", e); - LOGGER.error(String.format("%s.isSignatureValid(%s)", cert.getSerialNumber(), + LOGGER.info("Exception thrown while verifying certificate", e); + LOGGER.info(String.format("%s.isSignatureValid(%s)", cert.getSerialNumber(), signingKey.getFormat())); return false; } From 1eca360a4dadcef5491467c855102f9b3695004e Mon Sep 17 00:00:00 2001 From: chubtub <43381989+chubtub@users.noreply.github.com> Date: Wed, 17 Nov 2021 10:15:31 -0500 Subject: [PATCH 11/22] Modify log messages so that validation failures from certs in the keystore that are not actually in the chain do not raise undue attention --- .../main/java/hirs/data/persist/PCRPolicy.java | 3 +++ .../SupplyChainCredentialValidator.java | 16 ++++++++-------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/HIRS_Utils/src/main/java/hirs/data/persist/PCRPolicy.java b/HIRS_Utils/src/main/java/hirs/data/persist/PCRPolicy.java index 0a2dd19c..228d218c 100644 --- a/HIRS_Utils/src/main/java/hirs/data/persist/PCRPolicy.java +++ b/HIRS_Utils/src/main/java/hirs/data/persist/PCRPolicy.java @@ -197,6 +197,9 @@ public final class PCRPolicy extends Policy { String calculatedString = Hex.encodeHexString( pcrInfoShort.getCalculatedDigest()); validated = quoteString.contains(calculatedString); + if (!validated) { + LOGGER.warn(calculatedString + " not found in " + quoteString); + } } catch (NoSuchAlgorithmException naEx) { LOGGER.error(naEx); } diff --git a/HIRS_Utils/src/main/java/hirs/validation/SupplyChainCredentialValidator.java b/HIRS_Utils/src/main/java/hirs/validation/SupplyChainCredentialValidator.java index ce67ea39..abbdf84b 100644 --- a/HIRS_Utils/src/main/java/hirs/validation/SupplyChainCredentialValidator.java +++ b/HIRS_Utils/src/main/java/hirs/validation/SupplyChainCredentialValidator.java @@ -91,7 +91,7 @@ public final class SupplyChainCredentialValidator implements CredentialValidator "Platform credential attributes validated"; /** - * AppraisalStatus message for a valid platform credential appraisal. + * AppraisalStatus message for a valid firmware appraisal. */ public static final String FIRMWARE_VALID = "Firmware validated"; @@ -1630,15 +1630,15 @@ public final class SupplyChainCredentialValidator implements CredentialValidator cert.verify(signingCert.getPublicKey(), BouncyCastleProvider.PROVIDER_NAME); return true; } catch (InvalidKeyException e) { - LOGGER.warn("Incorrect key given to validate this cert's signature"); + LOGGER.info("Incorrect key given to validate this cert's signature"); } catch (CertificateException e) { - LOGGER.warn("Encoding error while validating this cert's signature"); + LOGGER.info("Encoding error while validating this cert's signature"); } catch (NoSuchAlgorithmException e) { - LOGGER.warn("Unsupported signature algorithm found during validation"); + LOGGER.info("Unsupported signature algorithm found during validation"); } catch (NoSuchProviderException e) { - LOGGER.warn("Incorrect provider for cert signature validation"); + LOGGER.info("Incorrect provider for cert signature validation"); } catch (SignatureException e) { - LOGGER.warn(String.format("%s.verify(%s)", cert.getSubjectDN(), + LOGGER.info(String.format("%s.verify(%s)", cert.getSubjectDN(), signingCert.getSubjectDN())); } return false; @@ -1685,8 +1685,8 @@ public final class SupplyChainCredentialValidator implements CredentialValidator new JcaContentVerifierProviderBuilder().setProvider("BC").build(signingKey); return cert.isSignatureValid(contentVerifierProvider); } catch (OperatorCreationException | CertException e) { - LOGGER.error("Exception thrown while verifying certificate", e); - LOGGER.error(String.format("%s.isSignatureValid(%s)", cert.getSerialNumber(), + LOGGER.info("Exception thrown while verifying certificate", e); + LOGGER.info(String.format("%s.isSignatureValid(%s)", cert.getSerialNumber(), signingKey.getFormat())); return false; } From 4c46758d9a8eef19e476b5269900b039ab1b7ab7 Mon Sep 17 00:00:00 2001 From: Cyrus <24922493+cyrus-dev@users.noreply.github.com> Date: Tue, 23 Nov 2021 09:36:00 -0500 Subject: [PATCH 12/22] This commit is a feature update. The IMA PCR enable/diable is being enhanced to update the mask the provisioner uses to pull the quote from the TPM. This code will send down a string range of PCR values that excludes PCR 10. The quote that is returned should be a composite without the PCR 10. There will be a log statement in this commit that should be removed. --- .../AbstractAttestationCertificateAuthority.java | 16 +++++++++++++--- HIRS_ProvisionerTPM2/include/Utils.h | 2 ++ HIRS_ProvisionerTPM2/src/ProvisionerTpm2.proto | 1 + .../src/RestfulClientProvisioner.cpp | 6 +++++- HIRS_ProvisionerTPM2/src/TPM2_Provisioner.cpp | 12 ++++++++---- HIRS_ProvisionerTPM2/src/Utils.cpp | 12 ++++++++++++ .../main/java/hirs/data/persist/PCRPolicy.java | 8 +++++++- 7 files changed, 48 insertions(+), 9 deletions(-) diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/AbstractAttestationCertificateAuthority.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/AbstractAttestationCertificateAuthority.java index 6aeab8d4..99762a96 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/AbstractAttestationCertificateAuthority.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/AbstractAttestationCertificateAuthority.java @@ -126,6 +126,8 @@ public abstract class AbstractAttestationCertificateAuthority = "/webapps/HIRS_AttestationCA/upload/"; private static final String PCR_UPLOAD_FOLDER = CATALINA_HOME + TOMCAT_UPLOAD_DIRECTORY; + private static final String PCR_QUOTE_MASK = "0,1,2,3,4,5,6,7,8,9,10,11,12,13," + + "14,15,16,17,18,19,20,21,22,23"; /** * Number of bytes to include in the TPM2.0 nonce. @@ -441,6 +443,8 @@ public abstract class AbstractAttestationCertificateAuthority RSAPublicKey akPub = parsePublicKey(claim.getAkPublicArea().toByteArray()); byte[] nonce = generateRandomBytes(NONCE_LENGTH); ByteString blobStr = tpm20MakeCredential(ekPub, akPub, nonce); + SupplyChainPolicy scp = this.supplyChainValidationService.getPolicy(); + String pcrQuoteMask = PCR_QUOTE_MASK; String strNonce = HexUtils.byteArrayToHexString(nonce); LOG.info("Sending nonce: " + strNonce); @@ -448,10 +452,14 @@ public abstract class AbstractAttestationCertificateAuthority tpm2ProvisionerStateDBManager.save(new TPM2ProvisionerState(nonce, identityClaim)); + if (scp != null && scp.isIgnoreImaEnabled()) { + pcrQuoteMask = PCR_QUOTE_MASK.replace("10,", ""); + } // Package response ProvisionerTpm2.IdentityClaimResponse response = ProvisionerTpm2.IdentityClaimResponse.newBuilder() - .setCredentialBlob(blobStr).build(); + .setCredentialBlob(blobStr).setMask(pcrQuoteMask) + .build(); return response.toByteArray(); } else { @@ -622,9 +630,11 @@ public abstract class AbstractAttestationCertificateAuthority tpm2ProvisionerStateDBManager.delete(tpm2ProvisionerState); // Package the signed certificate into a response - ByteString certificateBytes = ByteString.copyFrom(derEncodedAttestationCertificate); + ByteString certificateBytes = ByteString + .copyFrom(derEncodedAttestationCertificate); ProvisionerTpm2.CertificateResponse response = ProvisionerTpm2.CertificateResponse - .newBuilder().setCertificate(certificateBytes).build(); + .newBuilder().setCertificate(certificateBytes) + .build(); saveAttestationCertificate(derEncodedAttestationCertificate, endorsementCredential, platformCredentials, device); diff --git a/HIRS_ProvisionerTPM2/include/Utils.h b/HIRS_ProvisionerTPM2/include/Utils.h index 39099750..c02371f5 100644 --- a/HIRS_ProvisionerTPM2/include/Utils.h +++ b/HIRS_ProvisionerTPM2/include/Utils.h @@ -151,6 +151,8 @@ namespace string_utils { std::string trimWhitespaceFromRight(std::string str); std::string trimWhitespaceFromBothEnds(std::string str); + + std::vector split(const std::string& str, char delim); } // namespace string_utils } // namespace hirs diff --git a/HIRS_ProvisionerTPM2/src/ProvisionerTpm2.proto b/HIRS_ProvisionerTPM2/src/ProvisionerTpm2.proto index e8cd5766..62dadc2a 100644 --- a/HIRS_ProvisionerTPM2/src/ProvisionerTpm2.proto +++ b/HIRS_ProvisionerTPM2/src/ProvisionerTpm2.proto @@ -79,6 +79,7 @@ message TpmQuote { message IdentityClaimResponse { required bytes credential_blob = 1; + required string mask = 2; } message CertificateRequest { diff --git a/HIRS_ProvisionerTPM2/src/RestfulClientProvisioner.cpp b/HIRS_ProvisionerTPM2/src/RestfulClientProvisioner.cpp index e734f588..229a9a87 100644 --- a/HIRS_ProvisionerTPM2/src/RestfulClientProvisioner.cpp +++ b/HIRS_ProvisionerTPM2/src/RestfulClientProvisioner.cpp @@ -60,6 +60,7 @@ string RestfulClientProvisioner::sendIdentityClaim( } string identityClaimByteString; + string result; identityClaim.SerializeToString(&identityClaimByteString); // Send serialized Identity Claim to ACA @@ -86,13 +87,16 @@ string RestfulClientProvisioner::sendIdentityClaim( { // Convert the nonce blob to hex for logging string blobHex = binaryToHex(response.credential_blob()); + stringstream responses; + responses << response.credential_blob() << ";" << response.mask(); stringstream logStream; + result = responses.str(); logStream << "Received nonce blob: " << blobHex; LOGGER.info(logStream.str()); } // Return the wrapped nonce blob - return response.credential_blob(); + return result; } else { stringstream errormsg; diff --git a/HIRS_ProvisionerTPM2/src/TPM2_Provisioner.cpp b/HIRS_ProvisionerTPM2/src/TPM2_Provisioner.cpp index 7d2700e3..63555d63 100644 --- a/HIRS_ProvisionerTPM2/src/TPM2_Provisioner.cpp +++ b/HIRS_ProvisionerTPM2/src/TPM2_Provisioner.cpp @@ -39,6 +39,7 @@ using std::cerr; using std::endl; using std::string; using std::stringstream; +using std::vector; int provision() { Logger logger = Logger::getDefaultLogger(); @@ -128,8 +129,12 @@ int provision() { "TPM2_Provisioner.cpp", __LINE__); identityClaim.set_paccoroutput(paccorOutputString); RestfulClientProvisioner provisioner; - string nonceBlob = provisioner.sendIdentityClaim(identityClaim); - if (nonceBlob == "") { + string response = provisioner.sendIdentityClaim(identityClaim); + vector response_vector = hirs::string_utils::split(response, ';'); + + string nonceBlob = response_vector.at(0); + string mask = response_vector.at(1); + if (nonceBlob == "" || mask == "") { cout << "----> Provisioning failed." << endl; cout << "Please refer to the Attestation CA for details." << endl; return 0; @@ -152,8 +157,7 @@ int provision() { hirs::pb::CertificateRequest certificateRequest; certificateRequest.set_nonce(decryptedNonce); certificateRequest.set_quote(tpm2.getQuote( - "0,1,2,3,4,5,6,7,8,9,10,11,12,13," - "14,15,16,17,18,19,20,21,22,23", + mask, decryptedNonce)); const string& akCertificateByteString diff --git a/HIRS_ProvisionerTPM2/src/Utils.cpp b/HIRS_ProvisionerTPM2/src/Utils.cpp index d8f10b56..9001bcd1 100644 --- a/HIRS_ProvisionerTPM2/src/Utils.cpp +++ b/HIRS_ProvisionerTPM2/src/Utils.cpp @@ -293,6 +293,18 @@ namespace string_utils { return trimWhitespaceFromRight(trimWhitespaceFromLeft(str)); } + vector split(const string &str, char delim) { + vector result; + stringstream ss(str); + string item; + + while (getline(ss, item, delim)) { + result.push_back(item); + } + + return result; + } + } // namespace string_utils } // namespace hirs diff --git a/HIRS_Utils/src/main/java/hirs/data/persist/PCRPolicy.java b/HIRS_Utils/src/main/java/hirs/data/persist/PCRPolicy.java index 228d218c..3f440624 100644 --- a/HIRS_Utils/src/main/java/hirs/data/persist/PCRPolicy.java +++ b/HIRS_Utils/src/main/java/hirs/data/persist/PCRPolicy.java @@ -40,6 +40,7 @@ public final class PCRPolicy extends Policy { private static final int TBOOT_PCR_END = 19; // PCR 5 private static final int GPT_PCR = 5; + private static final int IMA_MASK = 0xfffbff; // Event Log Event Types private static final String EVT_EFI_BOOT = "EV_EFI_BOOT_SERVICES_APPLICATION"; @@ -169,6 +170,7 @@ public final class PCRPolicy extends Policy { boolean validated = false; short localityAtRelease = 0; String quoteString = new String(tpmQuote, StandardCharsets.UTF_8); + int pcrMaskSelection = PcrSelection.ALL_PCRS_ON; TPMMeasurementRecord[] measurements = new TPMMeasurementRecord[baselinePcrs.length]; try { @@ -179,7 +181,11 @@ public final class PCRPolicy extends Policy { LOGGER.error(deEx); } - PcrSelection pcrSelection = new PcrSelection(PcrSelection.ALL_PCRS_ON); + if (this.enableIgnoreIma) { + pcrMaskSelection = IMA_MASK; + } + + PcrSelection pcrSelection = new PcrSelection(pcrMaskSelection); PcrComposite pcrComposite = new PcrComposite( pcrSelection, Arrays.asList(measurements)); From cf4e641273e23b6a27f2b30041833a6119f38c15 Mon Sep 17 00:00:00 2001 From: Cyrus <24922493+cyrus-dev@users.noreply.github.com> Date: Tue, 23 Nov 2021 09:40:11 -0500 Subject: [PATCH 13/22] Added log statement for quote. --- .../attestationca/AbstractAttestationCertificateAuthority.java | 1 + 1 file changed, 1 insertion(+) diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/AbstractAttestationCertificateAuthority.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/AbstractAttestationCertificateAuthority.java index 99762a96..637e0702 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/AbstractAttestationCertificateAuthority.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/AbstractAttestationCertificateAuthority.java @@ -598,6 +598,7 @@ public abstract class AbstractAttestationCertificateAuthority // Parse through the Provisioner supplied TPM Quote and pcr values // these fields are optional if (request.getQuote() != null && !request.getQuote().isEmpty()) { + LOG.error(String.format("TPM Quote:\n%s", request.getQuote())); parseTPMQuote(request.getQuote().toStringUtf8()); TPMInfo savedInfo = device.getDeviceInfo().getTPMInfo(); TPMInfo tpmInfo = new TPMInfo(savedInfo.getTPMMake(), From 483099a2737e839531e7eeed4f79e54b20fc2bd9 Mon Sep 17 00:00:00 2001 From: Cyrus <24922493+cyrus-dev@users.noreply.github.com> Date: Tue, 23 Nov 2021 10:06:21 -0500 Subject: [PATCH 14/22] Updated the policy code to modify the measurement array length and ignore PCR 10 as well. --- .../main/java/hirs/data/persist/PCRPolicy.java | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/HIRS_Utils/src/main/java/hirs/data/persist/PCRPolicy.java b/HIRS_Utils/src/main/java/hirs/data/persist/PCRPolicy.java index 3f440624..4e8f1263 100644 --- a/HIRS_Utils/src/main/java/hirs/data/persist/PCRPolicy.java +++ b/HIRS_Utils/src/main/java/hirs/data/persist/PCRPolicy.java @@ -171,20 +171,26 @@ public final class PCRPolicy extends Policy { short localityAtRelease = 0; String quoteString = new String(tpmQuote, StandardCharsets.UTF_8); int pcrMaskSelection = PcrSelection.ALL_PCRS_ON; + int recordLength = baselinePcrs.length; - TPMMeasurementRecord[] measurements = new TPMMeasurementRecord[baselinePcrs.length]; + if (enableIgnoreIma) { + pcrMaskSelection = IMA_MASK; + recordLength--; + } + + TPMMeasurementRecord[] measurements = new TPMMeasurementRecord[recordLength]; try { for (int i = 0; i <= TPMMeasurementRecord.MAX_PCR_ID; i++) { - measurements[i] = new TPMMeasurementRecord(i, storedPcrs[i]); + if (i == 10 && enableIgnoreIma) { + LOGGER.info("Ignore IMA PCR policy is enabled."); + } else { + measurements[i] = new TPMMeasurementRecord(i, storedPcrs[i]); + } } } catch (DecoderException deEx) { LOGGER.error(deEx); } - if (this.enableIgnoreIma) { - pcrMaskSelection = IMA_MASK; - } - PcrSelection pcrSelection = new PcrSelection(pcrMaskSelection); PcrComposite pcrComposite = new PcrComposite( pcrSelection, From 95c5e40f89717b69a2a0d88020d77355072b3991 Mon Sep 17 00:00:00 2001 From: iadgovuser29 <33426478+iadgovuser29@users.noreply.github.com> Date: Tue, 23 Nov 2021 22:01:16 -0500 Subject: [PATCH 15/22] Utilize protobuf to parse claim response. Work on array handling on ACA. --- ...stractAttestationCertificateAuthority.java | 2 +- .../SupplyChainValidationServiceImpl.java | 8 ++--- .../src/ProvisionerTpm2.proto | 2 +- .../src/RestfulClientProvisioner.cpp | 31 ++++++++++--------- HIRS_ProvisionerTPM2/src/TPM2_Provisioner.cpp | 17 +++++----- .../java/hirs/data/persist/PCRPolicy.java | 16 +++++----- 6 files changed, 39 insertions(+), 37 deletions(-) diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/AbstractAttestationCertificateAuthority.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/AbstractAttestationCertificateAuthority.java index 637e0702..451bfae1 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/AbstractAttestationCertificateAuthority.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/AbstractAttestationCertificateAuthority.java @@ -458,7 +458,7 @@ public abstract class AbstractAttestationCertificateAuthority // Package response ProvisionerTpm2.IdentityClaimResponse response = ProvisionerTpm2.IdentityClaimResponse.newBuilder() - .setCredentialBlob(blobStr).setMask(pcrQuoteMask) + .setCredentialBlob(blobStr).setPcrMask(pcrQuoteMask) .build(); return response.toByteArray(); diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/service/SupplyChainValidationServiceImpl.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/service/SupplyChainValidationServiceImpl.java index 913f6e00..1f4dbb6d 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/service/SupplyChainValidationServiceImpl.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/service/SupplyChainValidationServiceImpl.java @@ -520,9 +520,9 @@ public class SupplyChainValidationServiceImpl implements SupplyChainValidationSe + "provide pcr values.", device.getName())); } else { // we have a full set of PCR values - int algorithmLength = baseline[0].length(); - String[] storedPcrs = buildStoredPcrs(pcrContent, algorithmLength); - pcrPolicy.validatePcrs(storedPcrs); + //int algorithmLength = baseline[0].length(); + //String[] storedPcrs = buildStoredPcrs(pcrContent, algorithmLength); + //pcrPolicy.validatePcrs(storedPcrs); // part 2 of firmware validation check: bios measurements // vs baseline tcg event log @@ -606,7 +606,7 @@ public class SupplyChainValidationServiceImpl implements SupplyChainValidationSe SupplyChainValidationSummary summary = null; Level level = Level.ERROR; AppraisalStatus fwStatus = new AppraisalStatus(FAIL, - SupplyChainCredentialValidator.FIRMWARE_VALID); + "Unknown exception caught during quote validation."); SupportReferenceManifest sRim = null; EventLogMeasurements eventLog = null; diff --git a/HIRS_ProvisionerTPM2/src/ProvisionerTpm2.proto b/HIRS_ProvisionerTPM2/src/ProvisionerTpm2.proto index 62dadc2a..fdf30985 100644 --- a/HIRS_ProvisionerTPM2/src/ProvisionerTpm2.proto +++ b/HIRS_ProvisionerTPM2/src/ProvisionerTpm2.proto @@ -79,7 +79,7 @@ message TpmQuote { message IdentityClaimResponse { required bytes credential_blob = 1; - required string mask = 2; + optional string pcr_mask = 2; } message CertificateRequest { diff --git a/HIRS_ProvisionerTPM2/src/RestfulClientProvisioner.cpp b/HIRS_ProvisionerTPM2/src/RestfulClientProvisioner.cpp index 229a9a87..1f77c53f 100644 --- a/HIRS_ProvisionerTPM2/src/RestfulClientProvisioner.cpp +++ b/HIRS_ProvisionerTPM2/src/RestfulClientProvisioner.cpp @@ -60,7 +60,6 @@ string RestfulClientProvisioner::sendIdentityClaim( } string identityClaimByteString; - string result; identityClaim.SerializeToString(&identityClaimByteString); // Send serialized Identity Claim to ACA @@ -82,21 +81,25 @@ string RestfulClientProvisioner::sendIdentityClaim( } IdentityClaimResponse response; - response.ParseFromString(r.text); - - { - // Convert the nonce blob to hex for logging - string blobHex = binaryToHex(response.credential_blob()); - stringstream responses; - responses << response.credential_blob() << ";" << response.mask(); - stringstream logStream; - result = responses.str(); - logStream << "Received nonce blob: " << blobHex; - LOGGER.info(logStream.str()); + try { + response.ParseFromString(r.text); + } catch (const google::protobuf::FatalException& e) { + LOGGER.error(e.what()); + stringstream errormsg; + errormsg << "Provisioning failed. IdentityClaimResponse " + << "did not contain credential_blob."; + throw HirsRuntimeException(errormsg.str(), + "RestfulClientProvisioner::sendIdentityClaim"); } - // Return the wrapped nonce blob - return result; + // Convert the nonce blob to hex for logging + string blobHex = binaryToHex(response.credential_blob()); + stringstream logStream; + logStream << "Received nonce blob: " << blobHex; + LOGGER.info(logStream.str()); + + // Return the response + return response.SerializeAsString(); } else { stringstream errormsg; diff --git a/HIRS_ProvisionerTPM2/src/TPM2_Provisioner.cpp b/HIRS_ProvisionerTPM2/src/TPM2_Provisioner.cpp index 63555d63..be8fffb7 100644 --- a/HIRS_ProvisionerTPM2/src/TPM2_Provisioner.cpp +++ b/HIRS_ProvisionerTPM2/src/TPM2_Provisioner.cpp @@ -130,16 +130,15 @@ int provision() { identityClaim.set_paccoroutput(paccorOutputString); RestfulClientProvisioner provisioner; string response = provisioner.sendIdentityClaim(identityClaim); - vector response_vector = hirs::string_utils::split(response, ';'); - - string nonceBlob = response_vector.at(0); - string mask = response_vector.at(1); - if (nonceBlob == "" || mask == "") { + hirs::pb::IdentityClaimResponse icr; + if (!icr.ParseFromString(response) || !icr.has_credential_blob()) { cout << "----> Provisioning failed." << endl; - cout << "Please refer to the Attestation CA for details." << endl; + cout << "The ACA did not send make credential information." << endl; return 0; } + string nonceBlob = icr.credential_blob(); + // activateIdentity requires we read makeCredential output from a file cout << "----> Received response. Attempting to decrypt nonce" << endl; try { @@ -157,8 +156,10 @@ int provision() { hirs::pb::CertificateRequest certificateRequest; certificateRequest.set_nonce(decryptedNonce); certificateRequest.set_quote(tpm2.getQuote( - mask, - decryptedNonce)); + icr.has_pcr_mask() + ? icr.pcr_mask() + : "0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23", + decryptedNonce)); const string& akCertificateByteString = provisioner.sendAttestationCertificateRequest(certificateRequest); diff --git a/HIRS_Utils/src/main/java/hirs/data/persist/PCRPolicy.java b/HIRS_Utils/src/main/java/hirs/data/persist/PCRPolicy.java index 4e8f1263..2312d345 100644 --- a/HIRS_Utils/src/main/java/hirs/data/persist/PCRPolicy.java +++ b/HIRS_Utils/src/main/java/hirs/data/persist/PCRPolicy.java @@ -13,7 +13,7 @@ import javax.persistence.Column; import javax.persistence.Entity; import java.nio.charset.StandardCharsets; import java.security.NoSuchAlgorithmException; -import java.util.Arrays; +import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -171,20 +171,19 @@ public final class PCRPolicy extends Policy { short localityAtRelease = 0; String quoteString = new String(tpmQuote, StandardCharsets.UTF_8); int pcrMaskSelection = PcrSelection.ALL_PCRS_ON; - int recordLength = baselinePcrs.length; if (enableIgnoreIma) { pcrMaskSelection = IMA_MASK; - recordLength--; } - TPMMeasurementRecord[] measurements = new TPMMeasurementRecord[recordLength]; + ArrayList measurements = new ArrayList<>(); + try { - for (int i = 0; i <= TPMMeasurementRecord.MAX_PCR_ID; i++) { - if (i == 10 && enableIgnoreIma) { + for (int i = 0; i < storedPcrs.length; i++) { + if (i == IMA_PCR && enableIgnoreIma) { LOGGER.info("Ignore IMA PCR policy is enabled."); } else { - measurements[i] = new TPMMeasurementRecord(i, storedPcrs[i]); + measurements.add(new TPMMeasurementRecord(i, storedPcrs[i])); } } } catch (DecoderException deEx) { @@ -193,8 +192,7 @@ public final class PCRPolicy extends Policy { PcrSelection pcrSelection = new PcrSelection(pcrMaskSelection); PcrComposite pcrComposite = new PcrComposite( - pcrSelection, - Arrays.asList(measurements)); + pcrSelection, measurements); PcrInfoShort pcrInfoShort = new PcrInfoShort(pcrSelection, localityAtRelease, tpmQuote, pcrComposite); From 139e4c897263e3422b15137c89f78bca99aee99f Mon Sep 17 00:00:00 2001 From: Cyrus <24922493+cyrus-dev@users.noreply.github.com> Date: Wed, 24 Nov 2021 14:27:15 -0500 Subject: [PATCH 16/22] Added null pointer check to RDR variable. --- ...stractAttestationCertificateAuthority.java | 35 ++++++++++--------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/AbstractAttestationCertificateAuthority.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/AbstractAttestationCertificateAuthority.java index 451bfae1..1a2b6387 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/AbstractAttestationCertificateAuthority.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/AbstractAttestationCertificateAuthority.java @@ -598,7 +598,6 @@ public abstract class AbstractAttestationCertificateAuthority // Parse through the Provisioner supplied TPM Quote and pcr values // these fields are optional if (request.getQuote() != null && !request.getQuote().isEmpty()) { - LOG.error(String.format("TPM Quote:\n%s", request.getQuote())); parseTPMQuote(request.getQuote().toStringUtf8()); TPMInfo savedInfo = device.getDeviceInfo().getTPMInfo(); TPMInfo tpmInfo = new TPMInfo(savedInfo.getTPMMake(), @@ -1028,23 +1027,25 @@ public abstract class AbstractAttestationCertificateAuthority } } } else if (dbSupport.isSwidSupplemental() && !dbSupport.isProcessed()) { - try { - TCGEventLog logProcessor = new TCGEventLog(dbSupport.getRimBytes()); - ReferenceDigestValue rdv; - for (TpmPcrEvent tpe : logProcessor.getEventList()) { - rdv = new ReferenceDigestValue(rdr.getId(), tpe.getPcrIndex(), - tpe.getEventDigestStr(), tpe.getEventTypeStr(), - false, false); - this.referenceEventManager.saveValue(rdv); + if (rdr != null) { + try { + TCGEventLog logProcessor = new TCGEventLog(dbSupport.getRimBytes()); + ReferenceDigestValue rdv; + for (TpmPcrEvent tpe : logProcessor.getEventList()) { + rdv = new ReferenceDigestValue(rdr.getId(), tpe.getPcrIndex(), + tpe.getEventDigestStr(), tpe.getEventTypeStr(), + false, false); + this.referenceEventManager.saveValue(rdv); + } + dbSupport.setProcessed(true); + this.referenceManifestManager.update(dbSupport); + } catch (CertificateException cEx) { + LOG.error(cEx); + } catch (NoSuchAlgorithmException noSaEx) { + LOG.error(noSaEx); + } catch (IOException ioEx) { + LOG.error(ioEx); } - dbSupport.setProcessed(true); - this.referenceManifestManager.update(dbSupport); - } catch (CertificateException cEx) { - LOG.error(cEx); - } catch (NoSuchAlgorithmException noSaEx) { - LOG.error(noSaEx); - } catch (IOException ioEx) { - LOG.error(ioEx); } } } From 68be67b73a2a1db7b8dc1c61f1ffe79a3ece5d5b Mon Sep 17 00:00:00 2001 From: Cyrus <24922493+cyrus-dev@users.noreply.github.com> Date: Wed, 1 Dec 2021 13:57:18 -0500 Subject: [PATCH 17/22] Added default values to the provisioner for tcg certs and rim files --- HIRS_ProvisionerTPM2/src/TPM2_Provisioner.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/HIRS_ProvisionerTPM2/src/TPM2_Provisioner.cpp b/HIRS_ProvisionerTPM2/src/TPM2_Provisioner.cpp index be8fffb7..19b3259a 100644 --- a/HIRS_ProvisionerTPM2/src/TPM2_Provisioner.cpp +++ b/HIRS_ProvisionerTPM2/src/TPM2_Provisioner.cpp @@ -68,7 +68,7 @@ int provision() { // if platformCredential is empty, not in TPM // pull from properties file if (platformCredential.empty()) { - const std::string& cert_dir = props.get("tcg.cert.dir", ""); + const std::string& cert_dir = props.get("tcg.cert.dir", "/boot/tcg/cert/platform/"); try { platformCredentials = hirs::file_utils::search_directory(cert_dir); @@ -86,9 +86,9 @@ int provision() { // collect TCG Boot files std::vector rim_files; std::vector swidtag_files; - const std::string& rim_dir = props.get("tcg.rim.dir", ""); - const std::string& swid_dir = props.get("tcg.swidtag.dir", ""); - const std::string& live_log_file = props.get("tcg.event.file", ""); + const std::string& rim_dir = props.get("tcg.rim.dir", "/boot/tcg/manifest/rim/"); + const std::string& swid_dir = props.get("tcg.swidtag.dir", "/boot/tcg/manifest/swidtag/"); + const std::string& live_log_file = props.get("tcg.event.file", "/sys/kernel/security/tpm0/binary_bios_measurements"); try { rim_files = hirs::file_utils::search_directory(rim_dir); From a5c5a3ac60ab766bc6fb85701818352092d08c82 Mon Sep 17 00:00:00 2001 From: iadgovuser29 <33426478+iadgovuser29@users.noreply.github.com> Date: Thu, 2 Dec 2021 12:28:41 -0500 Subject: [PATCH 18/22] Add additional location for pci.ids file and fix checkstyle issues from previous commit. --- .../attestationca/portal/util/PciIds.java | 1 + HIRS_ProvisionerTPM2/src/TPM2_Provisioner.cpp | 20 +++++++++++++++---- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/util/PciIds.java b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/util/PciIds.java index 54bb4045..e7d7ee24 100644 --- a/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/util/PciIds.java +++ b/HIRS_AttestationCAPortal/src/main/java/hirs/attestationca/portal/util/PciIds.java @@ -30,6 +30,7 @@ public final class PciIds { { add("/usr/share/hwdata/pci.ids"); add("/usr/share/misc/pci.ids"); + add("/tmp/pci.ids"); } }); diff --git a/HIRS_ProvisionerTPM2/src/TPM2_Provisioner.cpp b/HIRS_ProvisionerTPM2/src/TPM2_Provisioner.cpp index 19b3259a..c7e90f3e 100644 --- a/HIRS_ProvisionerTPM2/src/TPM2_Provisioner.cpp +++ b/HIRS_ProvisionerTPM2/src/TPM2_Provisioner.cpp @@ -68,7 +68,10 @@ int provision() { // if platformCredential is empty, not in TPM // pull from properties file if (platformCredential.empty()) { - const std::string& cert_dir = props.get("tcg.cert.dir", "/boot/tcg/cert/platform/"); + const std::string& cert_dir = + props.get( + "tcg.cert.dir", + "/boot/tcg/cert/platform/"); try { platformCredentials = hirs::file_utils::search_directory(cert_dir); @@ -86,9 +89,18 @@ int provision() { // collect TCG Boot files std::vector rim_files; std::vector swidtag_files; - const std::string& rim_dir = props.get("tcg.rim.dir", "/boot/tcg/manifest/rim/"); - const std::string& swid_dir = props.get("tcg.swidtag.dir", "/boot/tcg/manifest/swidtag/"); - const std::string& live_log_file = props.get("tcg.event.file", "/sys/kernel/security/tpm0/binary_bios_measurements"); + const std::string& rim_dir = + props.get( + "tcg.rim.dir", + "/boot/tcg/manifest/rim/"); + const std::string& swid_dir = + props.get( + "tcg.swidtag.dir", + "/boot/tcg/manifest/swidtag/"); + const std::string& live_log_file = + props.get( + "tcg.event.file", + "/sys/kernel/security/tpm0/binary_bios_measurements"); try { rim_files = hirs::file_utils::search_directory(rim_dir); From 6337367ba9fba14b04ad02d6252b4521c20f839c Mon Sep 17 00:00:00 2001 From: Cyrus <24922493+cyrus-dev@users.noreply.github.com> Date: Fri, 3 Dec 2021 16:01:04 -0500 Subject: [PATCH 19/22] Added Pci IDs translate to the HIRS_Util module for the supply chain validator process so that the hash can match up for highlighting failed components. --- HIRS_Utils/build.gradle | 2 + .../java/hirs/tpm/TPMBaselineGenerator.java | 2 +- .../src/main/java/hirs/utils/PciIds.java | 194 ++++++++++++++++++ .../SupplyChainCredentialValidator.java | 7 +- 4 files changed, 203 insertions(+), 2 deletions(-) create mode 100644 HIRS_Utils/src/main/java/hirs/utils/PciIds.java diff --git a/HIRS_Utils/build.gradle b/HIRS_Utils/build.gradle index 54b7a0bc..348632fe 100644 --- a/HIRS_Utils/build.gradle +++ b/HIRS_Utils/build.gradle @@ -39,6 +39,7 @@ dependencies { compile libs.joda_time compile libs.log4j2 compile libs.mariadb + compile libs.pci_ids compile libs.reflections compile libs.guava compile libs.spring_core @@ -48,6 +49,7 @@ dependencies { exclude group: 'junit' } compile 'org.jboss.logging:jboss-logging:3.2.0.Final' + compile 'org.apache.commons:commons-text:1.9' // add spring plugin, but do not pull transitive dependencies (causes conflicts) compile(libs.spring_plugin) { diff --git a/HIRS_Utils/src/main/java/hirs/tpm/TPMBaselineGenerator.java b/HIRS_Utils/src/main/java/hirs/tpm/TPMBaselineGenerator.java index 681e9b3e..111b7734 100644 --- a/HIRS_Utils/src/main/java/hirs/tpm/TPMBaselineGenerator.java +++ b/HIRS_Utils/src/main/java/hirs/tpm/TPMBaselineGenerator.java @@ -15,7 +15,7 @@ import hirs.data.persist.TPMReport; import hirs.data.persist.baseline.TpmBlackListBaseline; import hirs.data.persist.baseline.TpmWhiteListBaseline; import org.apache.commons.codec.binary.Hex; -import org.apache.commons.lang3.StringEscapeUtils; +import org.apache.commons.text.StringEscapeUtils; import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; diff --git a/HIRS_Utils/src/main/java/hirs/utils/PciIds.java b/HIRS_Utils/src/main/java/hirs/utils/PciIds.java new file mode 100644 index 00000000..1e91cf90 --- /dev/null +++ b/HIRS_Utils/src/main/java/hirs/utils/PciIds.java @@ -0,0 +1,194 @@ +package hirs.utils; + +import com.github.marandus.pciid.model.Device; +import com.github.marandus.pciid.model.Vendor; +import com.github.marandus.pciid.service.PciIdsDatabase; +import com.google.common.base.Strings; +import hirs.data.persist.certificate.attributes.ComponentIdentifier; +import hirs.data.persist.certificate.attributes.V2.ComponentIdentifierV2; +import org.bouncycastle.asn1.DERUTF8String; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Collections; +import java.util.List; +import java.util.Vector; + +/** + * Provide Java access to PCI IDs. + */ +public final class PciIds { + /** + * This pci ids file can be in different places on different distributions. + */ + public static final List PCI_IDS_PATH = + Collections.unmodifiableList(new Vector() { + private static final long serialVersionUID = 1L; + { + add("/usr/share/hwdata/pci.ids"); + add("/usr/share/misc/pci.ids"); + add("/tmp/pci.ids"); + } + }); + + /** + * The PCI IDs Database object. + * + * This only needs to be loaded one time. + * + * The pci ids library protects the data inside the object by making it immutable. + */ + public static final PciIdsDatabase DB = new PciIdsDatabase(); + + static { + if (!DB.isReady()) { + String dbFile = null; + for (final String path : PCI_IDS_PATH) { + if ((new File(path)).exists()) { + dbFile = path; + break; + } + } + if (dbFile != null) { + InputStream is = null; + try { + is = new FileInputStream(new File(dbFile)); + DB.loadStream(is); + } catch (IOException e) { + // DB will not be ready, hardware IDs will not be translated + dbFile = null; + } finally { + if (is != null) { + try { + is.close(); + } catch (IOException e) { + dbFile = null; + } + } + } + } + } + } + + /** + * Utility class. + */ + private PciIds() { + } + + /** + * The Component Class TCG Registry OID. + */ + public static final String COMPCLASS_TCG_OID = "2.23.133.18.3.1"; + /** + * The Component Class Value mask for NICs. + */ + public static final String COMPCLASS_TCG_CAT_NIC = "00090000"; + /** + * The Component Class Value mask for GFX cards. + */ + public static final String COMPCLASS_TCG_CAT_GFX = "00050000"; + + /** + * Iterate through all components and translate PCI hardware IDs as necessary. It will only + * translate ComponentIdentifierV2+ objects as it relies on Component Class information. + * @param components List of ComponentIdentifiers. + * @return the translated list of ComponentIdentifiers. + */ + public static List translate( + final List components) { + Vector newList = new Vector(); + if (components != null && !components.isEmpty()) { + for (final ComponentIdentifier component : components) { + // V2 components should not be found alongside V1 components + // they pass through just in case + if (component.isVersion2()) { + newList.add(translate((ComponentIdentifierV2) component)); + } else { + newList.add(component); + } + } + } + return newList; + } + + /** + * Translate Vendor and Device IDs, if found, in ComponentIdentifierV2 objects. + * It will only translate ID values, any other value will pass through. + * @param component ComponentIdentifierV2 object. + * @return the translated ComponentIdentifierV2 object. + */ + public static ComponentIdentifierV2 translate(final ComponentIdentifierV2 component) { + ComponentIdentifierV2 newComponent = null; + if (component != null) { + newComponent = component; + // This can be updated as we get more accurate component class registries and values + // Component Class Registry not accessible: TCG assumed + final String compClassValue = component.getComponentClass().getCategoryValue(); + if (compClassValue.equals(COMPCLASS_TCG_CAT_NIC) + || compClassValue.equals(COMPCLASS_TCG_CAT_GFX)) { + DERUTF8String manufacturer = translateVendor(component.getComponentManufacturer()); + DERUTF8String model = translateDevice(component.getComponentManufacturer(), + component.getComponentModel()); + + newComponent = new ComponentIdentifierV2(component.getComponentClass(), + manufacturer, + model, + component.getComponentSerial(), + component.getComponentRevision(), + component.getComponentManufacturerId(), + component.getFieldReplaceable(), + component.getComponentAddress(), + component.getCertificateIdentifier(), + component.getComponentPlatformUri(), + component.getAttributeStatus()); + } + + } + return newComponent; + } + + /** + * Look up the vendor name from the PCI IDs list, if the input string contains an ID. + * If any part of this fails, return the original manufacturer value. + * @param refManufacturer DERUTF8String, likely from a ComponentIdentifier + * @return DERUTF8String with the discovered vendor name, or the original manufacturer value. + */ + public static DERUTF8String translateVendor(final DERUTF8String refManufacturer) { + DERUTF8String manufacturer = refManufacturer; + if (manufacturer != null && manufacturer.getString().trim().matches("^[0-9A-Fa-f]{4}$")) { + Vendor ven = DB.findVendor(manufacturer.getString().toLowerCase()); + if (ven != null && !Strings.isNullOrEmpty(ven.getName())) { + manufacturer = new DERUTF8String(ven.getName()); + } + } + return manufacturer; + } + + /** + * Look up the device name from the PCI IDs list, if the input strings contain IDs. + * The Device lookup requires the Vendor ID AND the Device ID to be valid values. + * If any part of this fails, return the original model value. + * @param refManufacturer DERUTF8String, likely from a ComponentIdentifier + * @param refModel DERUTF8String, likely from a ComponentIdentifier + * @return DERUTF8String with the discovered device name, or the original model value. + */ + public static DERUTF8String translateDevice(final DERUTF8String refManufacturer, + final DERUTF8String refModel) { + DERUTF8String manufacturer = refManufacturer; + DERUTF8String model = refModel; + if (manufacturer != null + && model != null + && manufacturer.getString().trim().matches("^[0-9A-Fa-f]{4}$") + && model.getString().trim().matches("^[0-9A-Fa-f]{4}$")) { + Device dev = DB.findDevice(manufacturer.getString().toLowerCase(), + model.getString().toLowerCase()); + if (dev != null && !Strings.isNullOrEmpty(dev.getName())) { + model = new DERUTF8String(dev.getName()); + } + } + return model; + } +} diff --git a/HIRS_Utils/src/main/java/hirs/validation/SupplyChainCredentialValidator.java b/HIRS_Utils/src/main/java/hirs/validation/SupplyChainCredentialValidator.java index abbdf84b..4b9985d4 100644 --- a/HIRS_Utils/src/main/java/hirs/validation/SupplyChainCredentialValidator.java +++ b/HIRS_Utils/src/main/java/hirs/validation/SupplyChainCredentialValidator.java @@ -13,6 +13,7 @@ import hirs.data.persist.certificate.attributes.ComponentIdentifier; import hirs.data.persist.certificate.attributes.V2.ComponentIdentifierV2; import hirs.data.persist.info.ComponentInfo; import hirs.data.persist.info.HardwareInfo; +import hirs.utils.PciIds; import org.apache.commons.codec.Charsets; import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.lang3.StringUtils; @@ -565,7 +566,11 @@ public final class SupplyChainCredentialValidator implements CredentialValidator // pass information of which ones failed in additionInfo for (ComponentIdentifier ci : validPcComponents) { - additionalInfo.append(String.format("%d;", ci.hashCode())); + ComponentIdentifierV2 pciCi = (ComponentIdentifierV2) ci; + if (PciIds.DB.isReady()) { + pciCi = PciIds.translate((ComponentIdentifierV2) ci); + } + additionalInfo.append(String.format("%d;", pciCi.hashCode())); } } From 9b790cb8054684d08477a057898ad3633e873f9f Mon Sep 17 00:00:00 2001 From: Cyrus <24922493+cyrus-dev@users.noreply.github.com> Date: Fri, 3 Dec 2021 17:01:18 -0500 Subject: [PATCH 20/22] checking for class cast now. Highlighting appears. --- .../SupplyChainCredentialValidator.java | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/HIRS_Utils/src/main/java/hirs/validation/SupplyChainCredentialValidator.java b/HIRS_Utils/src/main/java/hirs/validation/SupplyChainCredentialValidator.java index 4b9985d4..acd0a921 100644 --- a/HIRS_Utils/src/main/java/hirs/validation/SupplyChainCredentialValidator.java +++ b/HIRS_Utils/src/main/java/hirs/validation/SupplyChainCredentialValidator.java @@ -566,11 +566,19 @@ public final class SupplyChainCredentialValidator implements CredentialValidator // pass information of which ones failed in additionInfo for (ComponentIdentifier ci : validPcComponents) { - ComponentIdentifierV2 pciCi = (ComponentIdentifierV2) ci; - if (PciIds.DB.isReady()) { - pciCi = PciIds.translate((ComponentIdentifierV2) ci); + try { + if (ci.isVersion2()) { + ComponentIdentifierV2 pciCi = (ComponentIdentifierV2) ci; + if (PciIds.DB.isReady()) { + pciCi = PciIds.translate((ComponentIdentifierV2) ci); + } + additionalInfo.append(String.format("%d;", pciCi.hashCode())); + } else { + additionalInfo.append(String.format("%d;", ci.hashCode())); + } + } catch (Exception ex) { + LOGGER.error(ex.getMessage()); } - additionalInfo.append(String.format("%d;", pciCi.hashCode())); } } From e22d95c2e69c5317ffc93e6583fefa192fa0f04b Mon Sep 17 00:00:00 2001 From: iadgovuser29 <33426478+iadgovuser29@users.noreply.github.com> Date: Fri, 10 Dec 2021 09:07:28 -0500 Subject: [PATCH 21/22] Modified so PCI ID translation will highlight delta certs and show in Tooltips --- .../SupplyChainCredentialValidator.java | 75 +++++++++++-------- .../SupplyChainCredentialValidatorTest.java | 4 +- 2 files changed, 44 insertions(+), 35 deletions(-) diff --git a/HIRS_Utils/src/main/java/hirs/validation/SupplyChainCredentialValidator.java b/HIRS_Utils/src/main/java/hirs/validation/SupplyChainCredentialValidator.java index acd0a921..dc588634 100644 --- a/HIRS_Utils/src/main/java/hirs/validation/SupplyChainCredentialValidator.java +++ b/HIRS_Utils/src/main/java/hirs/validation/SupplyChainCredentialValidator.java @@ -565,20 +565,14 @@ public final class SupplyChainCredentialValidator implements CredentialValidator resultMessage.append(unmatchedComponents); // pass information of which ones failed in additionInfo + int counter = 0; for (ComponentIdentifier ci : validPcComponents) { - try { - if (ci.isVersion2()) { - ComponentIdentifierV2 pciCi = (ComponentIdentifierV2) ci; - if (PciIds.DB.isReady()) { - pciCi = PciIds.translate((ComponentIdentifierV2) ci); - } - additionalInfo.append(String.format("%d;", pciCi.hashCode())); - } else { - additionalInfo.append(String.format("%d;", ci.hashCode())); - } - } catch (Exception ex) { - LOGGER.error(ex.getMessage()); - } + counter++; + additionalInfo.append(String.format("%d;", ci.hashCode())); + } + if (counter > 0) { + additionalInfo.insert(0, "COMPID="); + additionalInfo.append(counter); } } @@ -710,6 +704,7 @@ public final class SupplyChainCredentialValidator implements CredentialValidator } if (!fieldValidation || !deltaSb.toString().isEmpty()) { + deltaSb.insert(0, "COMPID="); return new AppraisalStatus(FAIL, resultMessage.toString(), deltaSb.toString()); } @@ -729,21 +724,29 @@ public final class SupplyChainCredentialValidator implements CredentialValidator LOGGER.error("PACCOR output string:\n" + paccorOutputString); return new AppraisalStatus(ERROR, baseErrorMessage + ioEx.getMessage()); } + StringBuilder additionalInfo = new StringBuilder(); if (!fieldValidation) { - // instead of listing all unmatched, just print the #. The failure - // will link to the platform certificate that'll display them. - String failureResults = unmatchedComponents.substring(0, - unmatchedComponents.length() - 1); - String size = unmatchedComponents.substring(unmatchedComponents.length() - 1); resultMessage = new StringBuilder(); - - resultMessage.append(String.format("There are %s unmatched components " - + "on the Platform Certificate:%n", size)); + resultMessage.append("There are unmatched components:\n"); resultMessage.append(unmatchedComponents); - return new AppraisalStatus(FAIL, resultMessage.toString(), failureResults); + // pass information of which ones failed in additionInfo + int counter = 0; + for (ComponentIdentifier ci : baseCompList) { + counter++; + additionalInfo.append(String.format("%d;", ci.hashCode())); + } + if (counter > 0) { + additionalInfo.insert(0, "COMPID="); + additionalInfo.append(counter); + } + } + + if (fieldValidation) { + return new AppraisalStatus(PASS, PLATFORM_ATTRIBUTES_VALID); + } else { + return new AppraisalStatus(FAIL, resultMessage.toString(), additionalInfo.toString()); } - return new AppraisalStatus(PASS, PLATFORM_ATTRIBUTES_VALID); } private static String validateV2PlatformCredentialAttributes( @@ -775,14 +778,23 @@ public final class SupplyChainCredentialValidator implements CredentialValidator // now we return everything that was unmatched // what is in the component info/device reported components // is to be displayed as the failure + fullDeltaChainComponents.clear(); for (ComponentIdentifier ci : subCompIdList) { - ciV2 = (ComponentIdentifierV2) ci; - invalidPcIds.append(String.format("%d;", - ciV2.hashCode())); + if (ci.isVersion2() && PciIds.DB.isReady()) { + ci = PciIds.translate((ComponentIdentifierV2) ci); + } + LOGGER.error("Unmatched component: " + ci); + fullDeltaChainComponents.add(ci); + invalidPcIds.append(String.format( + "Manufacturer=%s, Model=%s, Serial=%s, Revision=%s;%n", + ci.getComponentManufacturer(), + ci.getComponentModel(), + ci.getComponentSerial(), + ci.getComponentRevision())); } } - return String.format("COMPID=%s%d", invalidPcIds.toString(), subCompIdList.size()); + return invalidPcIds.toString(); } /** @@ -848,7 +860,6 @@ public final class SupplyChainCredentialValidator implements CredentialValidator = allDeviceInfoComponents.stream().filter(componentInfo -> componentInfo.getComponentManufacturer().equals(pcManufacturer)) .collect(Collectors.toList()); - // For each component listed in the platform credential from this manufacturer // find the ones that specify a serial number so we can match the most specific ones // first. @@ -857,7 +868,6 @@ public final class SupplyChainCredentialValidator implements CredentialValidator -> compIdentifier.getComponentSerial() != null && StringUtils.isNotEmpty(compIdentifier.getComponentSerial().getString())) .collect(Collectors.toList()); - // Now match up the components from the device info that are from the same // manufacturer and have a serial number. As matches are found, remove them from // both lists. @@ -878,7 +888,6 @@ public final class SupplyChainCredentialValidator implements CredentialValidator } } } - // For each component listed in the platform credential from this manufacturer // find the ones that specify value for the revision field so we can match the most // specific ones first. @@ -887,7 +896,6 @@ public final class SupplyChainCredentialValidator implements CredentialValidator -> compIdentifier.getComponentRevision() != null && StringUtils.isNotEmpty(compIdentifier.getComponentRevision().getString())) .collect(Collectors.toList()); - // Now match up the components from the device info that are from the same // manufacturer and specify a value for the revision field. As matches are found, // remove them from both lists. @@ -908,7 +916,6 @@ public final class SupplyChainCredentialValidator implements CredentialValidator } } } - // The remaining components from the manufacturer have only the 2 required fields so // just match them. List templist = new ArrayList<>(pcComponentsFromManufacturer); @@ -934,6 +941,10 @@ public final class SupplyChainCredentialValidator implements CredentialValidator int unmatchedComponentCounter = 1; for (ComponentIdentifier unmatchedComponent : pcUnmatchedComponents) { + if (unmatchedComponent.isVersion2() && PciIds.DB.isReady()) { + unmatchedComponent = + PciIds.translate((ComponentIdentifierV2) unmatchedComponent); + } LOGGER.error("Unmatched component " + unmatchedComponentCounter++ + ": " + unmatchedComponent); sb.append(String.format("Manufacturer=%s, Model=%s, Serial=%s, Revision=%s;%n", diff --git a/HIRS_Utils/src/test/java/hirs/validation/SupplyChainCredentialValidatorTest.java b/HIRS_Utils/src/test/java/hirs/validation/SupplyChainCredentialValidatorTest.java index 49dd4c91..a6903420 100644 --- a/HIRS_Utils/src/test/java/hirs/validation/SupplyChainCredentialValidatorTest.java +++ b/HIRS_Utils/src/test/java/hirs/validation/SupplyChainCredentialValidatorTest.java @@ -2256,9 +2256,7 @@ public class SupplyChainCredentialValidatorTest { .validateDeltaPlatformCredentialAttributes(delta1, deviceInfoReport, base, chainCredentials); Assert.assertEquals(result.getAppStatus(), AppraisalStatus.Status.FAIL); - Assert.assertEquals(result.getMessage(), - "There are 1 unmatched components on the Platform Certificate:\n" - + "COMPID=370101885;1"); + Assert.assertEquals(result.getAdditionalInfo(), "COMPID=370101885;1"); } /** From da9c8469ba6bda4494bb8ced7e5c1bad3f0bb785 Mon Sep 17 00:00:00 2001 From: iadgovuser29 <33426478+iadgovuser29@users.noreply.github.com> Date: Fri, 10 Dec 2021 20:58:44 -0500 Subject: [PATCH 22/22] Re-creating the componentidentifer here lost important information. --- .../SupplyChainCredentialValidator.java | 27 +++++++++---------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/HIRS_Utils/src/main/java/hirs/validation/SupplyChainCredentialValidator.java b/HIRS_Utils/src/main/java/hirs/validation/SupplyChainCredentialValidator.java index dc588634..aa88382b 100644 --- a/HIRS_Utils/src/main/java/hirs/validation/SupplyChainCredentialValidator.java +++ b/HIRS_Utils/src/main/java/hirs/validation/SupplyChainCredentialValidator.java @@ -819,24 +819,23 @@ public final class SupplyChainCredentialValidator implements CredentialValidator // on the leftovers in the lists and the policy in place. final List pcComponents = new ArrayList<>(); for (ComponentIdentifier component : untrimmedPcComponents) { - DERUTF8String componentSerial = new DERUTF8String(""); - DERUTF8String componentRevision = new DERUTF8String(""); + if (component.getComponentManufacturer() != null) { + component.setComponentManufacturer(new DERUTF8String( + component.getComponentManufacturer().getString().trim())); + } + if (component.getComponentModel() != null) { + component.setComponentModel(new DERUTF8String( + component.getComponentModel().getString().trim())); + } if (component.getComponentSerial() != null) { - componentSerial = new DERUTF8String( - component.getComponentSerial().getString().trim()); + component.setComponentSerial(new DERUTF8String( + component.getComponentSerial().getString().trim())); } if (component.getComponentRevision() != null) { - componentRevision = new DERUTF8String( - component.getComponentRevision().getString().trim()); + component.setComponentRevision(new DERUTF8String( + component.getComponentRevision().getString().trim())); } - pcComponents.add( - new ComponentIdentifier( - new DERUTF8String(component.getComponentManufacturer().getString().trim()), - new DERUTF8String(component.getComponentModel().getString().trim()), - componentSerial, componentRevision, - component.getComponentManufacturerId(), - component.getFieldReplaceable(), - component.getComponentAddress())); + pcComponents.add(component); } LOGGER.info("Validating the following Platform Cert components...");