This is a complete refactor. This uses Hibernate 6, Springboot Framework

3, Java 17, Jakarta
This commit is contained in:
Cyrus 2023-02-14 07:30:16 -05:00
parent d18dc60adb
commit 72aa426018
1094 changed files with 7396 additions and 230756 deletions

View File

@ -1,89 +0,0 @@
apply plugin: 'war'
apply plugin: 'checkstyle'
evaluationDependsOn(':HIRS_Utils')
sourceCompatibility = 1.8
dependencies {
compile project(':TPM_Utils')
compile project(':HIRS_Structs')
compile project(':HIRS_Utils')
compile libs.bouncy_castle
compile libs.commons_codec
compile libs.commons_lang
compile libs.spring_webmvc
compile libs.log4j2
compile libs.log4j2_web
compile libs.protobuf_java
providedCompile libs.servlet_api
testCompile project(':HIRS_Utils').sourceSets.test.output
testCompile project(':HIRS_Utils').sourceSets.test.resources
testCompile libs.commons_lang
testCompile libs.spring_test
testCompile libs.mockito
testCompile libs.testng
testCompile libs.hsqldb
}
task generateProtoBuf(type:Exec) {
workingDir 'config'
commandLine './genJavaProtoBuf.sh'
}
compileJava.dependsOn generateProtoBuf
copyVersion.dependsOn compileJava
war.dependsOn copyVersion
ext.configDir = new File(projectDir, 'config')
ext.checkstyleConfigDir = "$configDir/checkstyle"
checkstyle {
toolVersion = '8.10.1'
configFile = checkstyleConfigFile
configProperties.put('basedir', checkstyleConfigDir)
ignoreFailures = false
showViolations = true
}
war {
from(buildDir) {
include 'VERSION'
into 'WEB-INF/classes'
}
archiveName = 'HIRS_AttestationCA.war'
}
publishing {
publications {
maven(MavenPublication) {
artifactId 'hirs-attestationca'
artifact jar
pom.withXml {
def dependenciesNode = asNode().appendNode('dependencies')
configurations.runtime.allDependencies.each {
if (it.group != null && it.name != null) {
def dependencyNode = dependenciesNode.appendNode('dependency')
dependencyNode.appendNode('groupId', it.group)
dependencyNode.appendNode('artifactId', it.name)
dependencyNode.appendNode('version', it.version)
if (it.excludeRules.size() > 0) {
def exclusionsNode = dependencyNode.appendNode('exclusions')
it.excludeRules.each { rule ->
def exclusionNode = exclusionsNode.appendNode('exclusion')
exclusionNode.appendNode('groupId', rule.group)
exclusionNode.appendNode('artifactId', rule.module)
}
}
}
}
}
}
}
}

View File

@ -1,14 +0,0 @@
<?xml version="1.0"?>
<!DOCTYPE suppressions PUBLIC
"-//Puppy Crawl//DTD Suppressions 1.1//EN"
"http://www.puppycrawl.com/dtds/suppressions_1_1.dtd">
<suppressions>
<suppress checks="MagicNumber" files=".*[/\\]src[/\\]test[/\\]+" />
<suppress checks="FinalParameters" files=".*[/\\]src[/\\]test[/\\]+" />
<suppress checks="JavadocPackage" files=".*[/\\]src[/\\]test[/\\]+" />
<suppress files="src/main/java/hirs/attestationca/configuration/provisionerTpm2/ProvisionerTpm2\.java" checks="[a-zA-Z0-9]*"/>
</suppressions>

View File

@ -1,69 +0,0 @@
package hirs.attestationca;
import hirs.appraiser.SupplyChainAppraiser;
import hirs.data.persist.DeviceGroup;
import hirs.data.persist.SupplyChainPolicy;
import hirs.persist.AppraiserManager;
import hirs.persist.DeviceGroupManager;
import hirs.persist.PolicyManager;
import static hirs.attestationca.AbstractAttestationCertificateAuthority.LOG;
/**
* Utility class that simply holds logic to seed the ACA's database with its
* default entries.
*/
public final class AcaDbInit {
// prevent construction
private AcaDbInit() { }
/**
* Insert the ACA's default entries into the DB. This class is invoked after successful
* install of the HIRS_AttestationCA RPM.
*
* @param appraiserManager the AppraiserManager to use to persist appraisers
* @param deviceGroupManager the DeviceGroupManager to use to persist device groups
* @param policyManager the PolicyManager to use to persist policies
*/
public static synchronized void insertDefaultEntries(
final AppraiserManager appraiserManager,
final DeviceGroupManager deviceGroupManager,
final PolicyManager policyManager) {
LOG.info("Ensuring default ACA database entries are present.");
// Ensure the default group exists. It may have already been created by the Server RPM
DeviceGroup defaultGroup = deviceGroupManager.getDeviceGroup(DeviceGroup.DEFAULT_GROUP);
if (defaultGroup == null) {
LOG.info("Default group not found; saving...");
defaultGroup = deviceGroupManager.saveDeviceGroup(new DeviceGroup(
DeviceGroup.DEFAULT_GROUP,
"This is the default group"));
LOG.info("Saved default group.");
}
// If the SupplyChainAppraiser exists, do not attempt to re-save the supply chain appraiser
// or SupplyChainPolicy
SupplyChainAppraiser supplyChainAppraiser = (SupplyChainAppraiser)
appraiserManager.getAppraiser(SupplyChainAppraiser.NAME);
if (supplyChainAppraiser != null) {
LOG.info("Supply chain appraiser is present; not inserting any more entries.");
LOG.info("ACA database initialization complete.");
return;
}
// Create the SupplyChainAppraiser
LOG.info("Saving supply chain appraiser...");
supplyChainAppraiser = (SupplyChainAppraiser)
appraiserManager.saveAppraiser(new SupplyChainAppraiser());
// Create the SupplyChainPolicy
LOG.info("Saving default supply chain policy...");
SupplyChainPolicy supplyChainPolicy = new SupplyChainPolicy(
SupplyChainPolicy.DEFAULT_POLICY);
policyManager.savePolicy(supplyChainPolicy);
policyManager.setDefaultPolicy(supplyChainAppraiser, supplyChainPolicy);
policyManager.setPolicy(supplyChainAppraiser, defaultGroup, supplyChainPolicy);
LOG.info("ACA database initialization complete.");
}
}

View File

@ -1,45 +0,0 @@
package hirs.attestationca;
/**
* A simple POJO that will provide a clean error message to clients making
* REST requests to the ACA. It is to be serialized to JSON for the return message.
*/
public class AcaRestError {
private String error;
/**
* Basic constructor necessary for Jackson JSON serialization to work properly.
*/
public AcaRestError() {
// Don't remove this constructor as it's required for JSON mapping
}
/**
* Parameterized constructor for creating this class normally.
*
* @param error the error message to store in this object
*/
public AcaRestError(final String error) {
this.error = error;
}
/**
* Simple getter to get the error message stored in this object.
*
* @return the error message
*/
public String getError() {
return error;
}
/**
* Simple setter to get the error message stored in this object.
*
* @param error the new error message to store in this object
*/
public void setError(final String error) {
this.error = error;
}
}

View File

@ -1,52 +0,0 @@
package hirs.attestationca;
/**
* Defines the responsibilities of the Attestation Certificate Authority.
*/
public interface AttestationCertificateAuthority {
/**
* The default size for IV blocks.
*/
int DEFAULT_IV_SIZE = 16;
/**
* Processes a given {@link hirs.structs.elements.aca.IdentityRequestEnvelope} and
* generates a {@link hirs.structs.elements.aca.IdentityResponseEnvelope}. In most cases,
* a client will generate the request using the TPM "Collate Identity" process.
*
* @param identityRequest generated during the collate identity process with a Tpm
* @return response for the request
*/
byte[] processIdentityRequest(byte[] identityRequest);
/**
* Processes a given
* {@link hirs.attestationca.configuration.provisionerTpm2.ProvisionerTpm2.IdentityClaim} and
* generates a response containing an encrypted nonce to be returned by the client in
* a future handshake request.
*
* @param identityClaim generated during the create identity claim process on a TPM2 Provisioner
* @return response for the request
*/
byte[] processIdentityClaimTpm2(byte[] identityClaim);
/**
* Processes a given
* {@link hirs.attestationca.configuration.provisionerTpm2.ProvisionerTpm2.CertificateRequest}
* and generates a response containing the signed, public certificate for
* the client's desired attestation key, if the correct nonce is supplied.
*
* @param certificateRequest request containing nonce from earlier identity
* claim handshake
* @return response for the request
*/
byte[] processCertificateRequest(byte[] certificateRequest);
/**
* Issues the PK of the ACA public/private key pair.
*
* @return public key of the attestation certificate authority
*/
byte[] getPublicKey();
}

View File

@ -1,154 +0,0 @@
package hirs.attestationca;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import hirs.data.persist.certificate.EndorsementCredential;
import hirs.data.persist.certificate.PlatformCredential;
import hirs.persist.CertificateManager;
import hirs.persist.DBManagerException;
import java.util.List;
import java.util.stream.Collectors;
/**
* Utility class which includes credential management functions used by the ACA.
*/
public final class CredentialManagementHelper {
private static final Logger LOG = LogManager.getLogger(CredentialManagementHelper.class);
private CredentialManagementHelper() {
}
/**
* Parses and stores the EK in the cert manager. If the cert is already present and archived,
* it is unarchived.
* @param certificateManager the certificate manager used for storage
* @param endorsementBytes the raw EK bytes used for parsing
* @return the parsed, valid EK
* @throws IllegalArgumentException if the provided bytes are not a valid EK.
*/
public static EndorsementCredential storeEndorsementCredential(
final CertificateManager certificateManager,
final byte[] endorsementBytes) throws IllegalArgumentException {
if (certificateManager == null) {
throw new IllegalArgumentException("null certificate manager");
}
if (endorsementBytes == null) {
throw new IllegalArgumentException("null endorsement credential bytes");
}
if (endorsementBytes.length <= 1) {
throw new IllegalArgumentException(
String.format("%d-length byte array given for endorsement credential",
endorsementBytes.length)
);
}
LOG.info("Parsing Endorsement Credential of length " + endorsementBytes.length);
EndorsementCredential endorsementCredential;
try {
endorsementCredential = EndorsementCredential
.parseWithPossibleHeader(endorsementBytes);
} catch (IllegalArgumentException iae) {
LOG.error(iae.getMessage());
throw iae;
}
int certificateHash = endorsementCredential.getCertificateHash();
EndorsementCredential existingCredential =
EndorsementCredential.select(certificateManager).includeArchived()
.byHashCode(certificateHash).getCertificate();
if (existingCredential == null) {
LOG.info("No Endorsement Credential found with hash: " + certificateHash);
return (EndorsementCredential) certificateManager.save(endorsementCredential);
} else if (existingCredential.isArchived()) {
// if the EK is stored in the DB and it's archived, unarchive.
LOG.info("Unarchiving credential");
existingCredential.restore();
existingCredential.resetCreateTime();
certificateManager.update(existingCredential);
}
return existingCredential;
}
/**
* Parses and stores the PC in the cert manager. If the cert is already present and archived,
* it is unarchived.
* @param certificateManager the certificate manager used for storage
* @param platformBytes the raw PC bytes used for parsing
* @return the parsed, valid PC, or null if the provided bytes are not a valid EK.
*/
public static PlatformCredential storePlatformCredential(
final CertificateManager certificateManager,
final byte[] platformBytes) {
if (certificateManager == null) {
throw new IllegalArgumentException("null certificate manager");
}
if (platformBytes == null) {
throw new IllegalArgumentException("null platform credential bytes");
}
if (platformBytes.length == 0) {
throw new IllegalArgumentException(
"zero-length byte array given for platform credential"
);
}
LOG.info("Parsing Platform Credential of length " + platformBytes.length);
try {
PlatformCredential platformCredential =
PlatformCredential.parseWithPossibleHeader(platformBytes);
if (platformCredential == null) {
return null;
}
PlatformCredential existingCredential =
PlatformCredential.select(certificateManager)
.includeArchived()
.byHashCode(platformCredential
.getCertificateHash())
.getCertificate();
if (existingCredential == null) {
if (platformCredential.getPlatformSerial() != null) {
List<PlatformCredential> certificates = PlatformCredential
.select(certificateManager)
.byBoardSerialNumber(platformCredential.getPlatformSerial())
.getCertificates().stream().collect(Collectors.toList());
if (!certificates.isEmpty()) {
// found associated certificates
for (PlatformCredential pc : certificates) {
if (pc.isBase() && platformCredential.isBase()) {
// found a base in the database associated with
// parsed certificate
LOG.error(String.format("Base certificate stored"
+ " in database with same platform"
+ "serial number. (%s)",
platformCredential.getPlatformSerial()));
return null;
}
}
}
}
return (PlatformCredential) certificateManager.save(platformCredential);
} else if (existingCredential.isArchived()) {
// if the PC is stored in the DB and it's archived, unarchive.
LOG.info("Unarchiving credential");
existingCredential.restore();
certificateManager.update(existingCredential);
return existingCredential;
}
return existingCredential;
} catch (DBManagerException dbe) {
LOG.error("Error retrieving or saving platform credential", dbe);
} catch (Exception e) {
LOG.error("Error parsing platform credential", e);
}
return null;
}
}

View File

@ -1,41 +0,0 @@
package hirs.attestationca;
import org.hibernate.SessionFactory;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.orm.hibernate4.LocalSessionFactoryBean;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import hirs.persist.DBAppraiserManager;
import hirs.persist.DBDeviceGroupManager;
import hirs.persist.DBPolicyManager;
import hirs.persist.PersistenceConfiguration;
import hirs.utils.HIRSProfiles;
/**
* Simply holds a contextInitialized method which will be called when the web app starts.
*/
public class InitializationListener implements ServletContextListener {
@Override
public void contextInitialized(final ServletContextEvent event) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.getEnvironment().addActiveProfile(HIRSProfiles.SERVER);
// register the database configuration and refresh the context
context.register(PersistenceConfiguration.class);
context.refresh();
// obtain reference to hibernate session factory
SessionFactory sessionFactory = context.getBean(LocalSessionFactoryBean.class).getObject();
AcaDbInit.insertDefaultEntries(
new DBAppraiserManager(sessionFactory),
new DBDeviceGroupManager(sessionFactory),
new DBPolicyManager(sessionFactory)
);
}
@Override
public void contextDestroyed(final ServletContextEvent event) {
}
}

View File

@ -1,200 +0,0 @@
package hirs.attestationca;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.ASN1OctetString;
import org.bouncycastle.asn1.DEROctetString;
import org.bouncycastle.asn1.DERUTF8String;
import org.bouncycastle.asn1.x500.AttributeTypeAndValue;
import org.bouncycastle.asn1.x500.RDN;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x500.X500NameBuilder;
import org.bouncycastle.asn1.x509.AuthorityKeyIdentifier;
import org.bouncycastle.asn1.x509.ExtendedKeyUsage;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.asn1.x509.Extensions;
import org.bouncycastle.asn1.x509.GeneralName;
import org.bouncycastle.asn1.x509.GeneralNames;
import org.bouncycastle.asn1.x509.GeneralNamesBuilder;
import org.bouncycastle.asn1.x509.KeyPurposeId;
import org.bouncycastle.asn1.x509.TBSCertificate;
import org.bouncycastle.asn1.x509.AttributeCertificateInfo;
import java.io.IOException;
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
import java.util.Collection;
import hirs.data.persist.certificate.EndorsementCredential;
import hirs.data.persist.certificate.PlatformCredential;
/**
* Builds extensions based on Platform and Endorsement credentials to provide in an issued
* certificate.
*/
public final class IssuedCertificateAttributeHelper {
private static final String TPM_ID_LABEL_OID = "2.23.133.2.15";
/**
* Object Identifier TCPA at TPM ID Label.
*/
public static final ASN1ObjectIdentifier TCPA_AT_TPM_ID_LABEL =
new ASN1ObjectIdentifier(TPM_ID_LABEL_OID);
/**
* The extended key usage extension.
*/
public static final Extension EXTENDED_KEY_USAGE_EXTENSION;
private static final Logger LOG = LogManager.getLogger(IssuedCertificateAttributeHelper.class);
private static final ASN1ObjectIdentifier TCG_KP_AIK_CERTIFICATE_ATTRIBUTE =
new ASN1ObjectIdentifier("2.23.133.8.3");
static {
// Generates an extension that identifies a cert as an AIK cert
Extension extension = null;
try {
extension = new Extension(Extension.extendedKeyUsage, true,
new ExtendedKeyUsage(new KeyPurposeId[] {
KeyPurposeId.getInstance(TCG_KP_AIK_CERTIFICATE_ATTRIBUTE)}).getEncoded());
} catch (IOException e) {
LOG.error("Error generating extended key usage extension");
}
EXTENDED_KEY_USAGE_EXTENSION = extension;
}
private IssuedCertificateAttributeHelper() {
// do not construct publicly
}
/**
* This method builds the AKI extension that will be stored in the generated
* Attestation Issued Certificate.
* @param endorsementCredential EK object to pull AKI from.
* @return the AKI extension.
* @throws IOException on bad get instance for AKI.
*/
public static Extension buildAuthorityKeyIdentifier(
final EndorsementCredential endorsementCredential) throws IOException {
if (endorsementCredential == null || endorsementCredential.getX509Certificate() == null) {
return null;
}
byte[] extValue = endorsementCredential.getX509Certificate()
.getExtensionValue(Extension.authorityKeyIdentifier.getId());
if (extValue == null) {
return null;
}
byte[] authExtension = ASN1OctetString.getInstance(extValue).getOctets();
AuthorityKeyIdentifier aki = AuthorityKeyIdentifier.getInstance(authExtension);
return new Extension(Extension.authorityKeyIdentifier, true, aki.getEncoded());
}
/**
* Builds the subject alternative name based on the supplied certificates.
* @param endorsementCredential the endorsement credential
* @param platformCredentials the platform credentials
* @param hostName the host name
* @return the subject alternative name extension
* @throws IOException an IO exception occurs building the extension
* @throws IllegalArgumentException if the host name is null
*/
public static Extension buildSubjectAlternativeNameFromCerts(
final EndorsementCredential endorsementCredential,
final Collection<PlatformCredential> platformCredentials, final String hostName)
throws IOException, IllegalArgumentException {
if (StringUtils.isEmpty(hostName)) {
LOG.error("null host name");
throw new IllegalArgumentException("must provide host name");
}
// assemble AIK cert SAN, using info from EC and PC
X500NameBuilder nameBuilder = new X500NameBuilder();
populateEndorsementCredentialAttributes(endorsementCredential, nameBuilder);
if (platformCredentials != null) {
for (PlatformCredential platformCredential : platformCredentials) {
populatePlatformCredentialAttributes(platformCredential, nameBuilder);
}
}
// add the OID for the TCG-required TPM ID label
DERUTF8String idLabel = new DERUTF8String(hostName);
nameBuilder.addRDN(new AttributeTypeAndValue(TCPA_AT_TPM_ID_LABEL, idLabel));
// put everything into the SAN, usable by the certificate builder
GeneralNamesBuilder genNamesBuilder = new GeneralNamesBuilder();
genNamesBuilder.addName(new GeneralName(nameBuilder.build()));
DEROctetString sanContent =
new DEROctetString(genNamesBuilder.build().getEncoded());
Extension subjectAlternativeName = new Extension(Extension.subjectAlternativeName,
true, sanContent);
return subjectAlternativeName;
}
private static void populatePlatformCredentialAttributes(
final PlatformCredential platformCredential,
final X500NameBuilder nameBuilder) throws IOException {
if (platformCredential == null) {
return;
}
final RDN[] rdns;
try {
LOG.debug("Applying platform credential attributes to SAN");
AttributeCertificateInfo platformCredentialAttributeHolders =
platformCredential.getAttributeCertificate().getAcinfo();
rdns = ((X500Name) GeneralNames.fromExtensions(
platformCredentialAttributeHolders.getExtensions(),
Extension.subjectAlternativeName).getNames()[0].getName()).getRDNs();
} catch (IllegalArgumentException e) {
LOG.error("Unable to extract attributes from platform credential", e);
return;
}
populateRdnAttributesInNameBuilder(nameBuilder, rdns);
}
private static void populateEndorsementCredentialAttributes(
final EndorsementCredential endorsementCredential, final X500NameBuilder nameBuilder) {
if (endorsementCredential == null) {
return;
}
final RDN[] rdns;
try {
LOG.debug("Applying endorsement credential attributes to SAN");
X509Certificate endorsementX509 = endorsementCredential.getX509Certificate();
TBSCertificate tbsCertificate = TBSCertificate.getInstance(
endorsementX509.getTBSCertificate());
Extensions extensions = tbsCertificate.getExtensions();
GeneralNames names = GeneralNames.fromExtensions(extensions,
Extension.subjectAlternativeName);
if (names != null) {
X500Name x500 = (X500Name) names.getNames()[0].getName();
rdns = x500.getRDNs();
populateRdnAttributesInNameBuilder(nameBuilder, rdns);
} else {
LOG.error("No RDNs in endorsement credential attributes");
return;
}
} catch (CertificateEncodingException e) {
LOG.error("Certificate encoding exception", e);
return;
} catch (IOException e) {
LOG.error("Error creating x509 cert from endorsement credential", e);
return;
}
}
private static void populateRdnAttributesInNameBuilder(final X500NameBuilder nameBuilder,
final RDN[] rdns) {
for (final RDN rdn : rdns) {
nameBuilder.addRDN(rdn.getTypesAndValues()[0]);
}
}
}

View File

@ -1,290 +0,0 @@
package hirs.attestationca.configuration;
import hirs.persist.DBDeviceGroupManager;
import hirs.persist.DBDeviceManager;
import hirs.persist.DBReferenceDigestManager;
import hirs.persist.DBReferenceEventManager;
import hirs.persist.DBReferenceManifestManager;
import hirs.persist.DeviceGroupManager;
import hirs.persist.DeviceManager;
import hirs.persist.HibernateConfiguration;
import hirs.persist.ReferenceDigestManager;
import hirs.persist.ReferenceEventManager;
import hirs.persist.ReferenceManifestManager;
import hirs.structs.converters.SimpleStructConverter;
import hirs.structs.converters.StructConverter;
import hirs.utils.LogConfigurationUtil;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.springframework.beans.factory.BeanInitializationException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.annotation.PropertySources;
import org.springframework.context.annotation.Scope;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.core.env.Environment;
import org.springframework.orm.hibernate4.LocalSessionFactoryBean;
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import javax.annotation.PostConstruct;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.PrivateKey;
import java.security.Security;
import java.security.cert.X509Certificate;
/**
* Provides application context configuration for the Attestation Certificate
* Authority application. The properties are processed in order and as such, the
* last property file read in will override properties that may had already been
* defined previously. In other words, the 'defaults.properties' file provides a
* basic standard of properties that can be overrode by the
*/
@Configuration
@PropertySources({
@PropertySource(value = "classpath:defaults.properties"),
// detects if file exists, if not, ignore errors
@PropertySource(value = "file:/etc/hirs/aca/aca.properties",
ignoreResourceNotFound = true)
})
@ComponentScan({ "hirs.attestationca", "hirs.attestationca.service", "hirs.attestationca.rest",
"hirs.validation", "hirs.data.service" })
@Import(HibernateConfiguration.class)
@EnableWebMvc
public class AttestationCertificateAuthorityConfiguration extends WebMvcConfigurerAdapter {
private static final Logger LOG
= LogManager.getLogger(AttestationCertificateAuthorityConfiguration.class);
static {
try {
LogConfigurationUtil.applyConfiguration();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private static final String CLIENT_FILES_PATH = "file:/etc/hirs/aca/client-files/";
@Value("${aca.directories.certificates}")
private String certificatesLocation;
@Value("${aca.keyStore.location}")
private String keyStoreLocation;
@Value("${aca.keyStore.password:''}")
private String keyStorePassword;
@Value("${aca.keyStore.alias}")
private String keyAlias;
@Autowired
private Environment environment;
@Autowired
private LocalSessionFactoryBean sessionFactory;
/**
* @return bean to resolve injected annotation.Value property expressions
* for beans.
*/
@Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
/**
* Initialization of the ACA. Detects environment and runs configuration
* methods as required. This method is intended to be invoked by the Spring
* application context.
*/
@PostConstruct
void initialize() {
// ensure that Bouncy Castle is registered as a security provider
Security.addProvider(new BouncyCastleProvider());
// obtain path to ACA configuration
Path certificatesPath = Paths.get(certificatesLocation);
// create base directories if they do not exist
try {
Files.createDirectories(certificatesPath);
} catch (IOException e) {
throw new BeanInitializationException(
"Encountered error while initializing ACA directories: " + e.getMessage(), e);
}
// create the ACA key store if it doesn't exist
Path keyStorePath = Paths.get(keyStoreLocation);
if (!Files.exists(keyStorePath)) {
throw new IllegalStateException(
String.format("ACA Key Store not found at %s. Consult the HIRS User "
+ "Guide for ACA installation instructions.", keyStoreLocation));
}
}
/**
* @return the {@link PrivateKey} of the ACA
*/
@Bean
public PrivateKey privateKey() {
// obtain the key store
KeyStore keyStore = keyStore();
try {
// load the key from the key store
PrivateKey acaKey = (PrivateKey) keyStore.getKey(keyAlias,
keyStorePassword.toCharArray());
// break early if the certificate is not available.
if (acaKey == null) {
throw new BeanInitializationException(String.format("Key with alias "
+ "%s was not in KeyStore %s. Ensure that the KeyStore has the "
+ "specified certificate. ", keyAlias, keyStoreLocation));
}
return acaKey;
} catch (Exception e) {
throw new BeanInitializationException("Encountered error loading ACA private key "
+ "from key store: " + e.getMessage(), e);
}
}
/**
* @return the {@link X509Certificate} of the ACA
*/
@Bean
public X509Certificate acaCertificate() {
KeyStore keyStore = keyStore();
try {
X509Certificate acaCertificate = (X509Certificate) keyStore.getCertificate(keyAlias);
// break early if the certificate is not available.
if (acaCertificate == null) {
throw new BeanInitializationException(String.format("Certificate with alias "
+ "%s was not in KeyStore %s. Ensure that the KeyStore has the "
+ "specified certificate. ", keyAlias, keyStoreLocation));
}
return acaCertificate;
} catch (KeyStoreException e) {
throw new BeanInitializationException("Encountered error loading ACA certificate "
+ "from key store: " + e.getMessage(), e);
}
}
/**
* @return the {@link java.security.KeyStore} that contains the certificates
* for the ACA.
*/
@Bean
public KeyStore keyStore() {
Path keyStorePath = Paths.get(keyStoreLocation);
// attempt to open the key store. if that fails, log a meaningful message before failing.
try {
KeyStore keyStore = KeyStore.getInstance("JKS");
keyStore.load(Files.newInputStream(keyStorePath), keyStorePassword.toCharArray());
return keyStore;
} catch (Exception e) {
LOG.error(String.format(
"Encountered error while loading ACA key store. The most common issue is "
+ "that configured password does not work on the configured key"
+ " store %s.", keyStorePath));
LOG.error(String.format("Exception message: %s", e.getMessage()));
throw new BeanInitializationException(e.getMessage(), e);
}
}
/**
* Prototyped {@link StructConverter}. In other words, all instances
* returned by this method will be configured identically, but subsequent
* invocations will return a new instance.
*
* @return ready to use {@link StructConverter}.
*/
@Bean
@Scope("prototype")
public static StructConverter structConverter() {
return new SimpleStructConverter();
}
/**
* Creates a {@link DeviceGroupManager} ready to use.
*
* @return {@link DeviceGroupManager}
*/
@Bean
public DeviceGroupManager deviceGroupManager() {
return new DBDeviceGroupManager(sessionFactory.getObject());
}
/**
* Creates a {@link DeviceManager} ready to use.
*
* @return {@link DeviceManager}
*/
@Bean
public DeviceManager deviceManager() {
return new DBDeviceManager(sessionFactory.getObject());
}
/**
* Creates a {@link ReferenceManifestManager} ready to use.
*
* @return {@link ReferenceManifestManager}
*/
@Bean
public ReferenceManifestManager referenceManifestManager() {
return new DBReferenceManifestManager(sessionFactory.getObject());
}
/**
* Creates a {@link ReferenceDigestManager} ready to use.
*
* @return {@link ReferenceDigestManager}
*/
@Bean
public ReferenceDigestManager referenceDigestManager() {
return new DBReferenceDigestManager(sessionFactory.getObject());
}
/**
* Creates a {@link ReferenceEventManager} ready to use.
*
* @return {@link ReferenceEventManager}
*/
@Bean
public ReferenceEventManager referenceEventManager() {
return new DBReferenceEventManager(sessionFactory.getObject());
}
@Override
public void addResourceHandlers(final ResourceHandlerRegistry resourceHandlerRegistry) {
resourceHandlerRegistry.addResourceHandler("/client-files/**")
.addResourceLocations(CLIENT_FILES_PATH);
}
@Override
public void configureDefaultServletHandling(final DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
}

View File

@ -1,4 +0,0 @@
/**
* Includes the Spring Framework application context configuration classes.
*/
package hirs.attestationca.configuration;

View File

@ -1,27 +0,0 @@
package hirs.attestationca.exceptions;
/**
* Generic exception thrown while a {@link hirs.attestationca.AttestationCertificateAuthority}
* is processing a newly created Attestation Certificate for a validated identity.
*/
public class CertificateProcessingException extends RuntimeException {
/**
* Constructs a generic instance of this exception using the specified reason.
*
* @param reason for the exception
*/
public CertificateProcessingException(final String reason) {
super(reason);
}
/**
* Constructs a instance of this exception with the specified reason and backing root
* exception.
*
* @param reason for this exception
* @param rootException causing this exception
*/
public CertificateProcessingException(final String reason, final Throwable rootException) {
super(reason, rootException);
}
}

View File

@ -1,27 +0,0 @@
package hirs.attestationca.exceptions;
/**
* Generic exception thrown while a {@link hirs.attestationca.AttestationCertificateAuthority}
* is processing a newly submitted Identity.
*/
public class IdentityProcessingException extends RuntimeException {
/**
* Constructs a generic instance of this exception using the specified reason.
*
* @param reason for the exception
*/
public IdentityProcessingException(final String reason) {
super(reason);
}
/**
* Constructs a instance of this exception with the specified reason and backing root
* exception.
*
* @param reason for this exception
* @param rootException causing this exception
*/
public IdentityProcessingException(final String reason, final Throwable rootException) {
super(reason, rootException);
}
}

View File

@ -1,27 +0,0 @@
package hirs.attestationca.exceptions;
/**
* Generic exception thrown when a {@link hirs.attestationca.AttestationCertificateAuthority}
* encounters an unexpected condition that can't be handled.
*/
public class UnexpectedServerException extends RuntimeException {
/**
* Constructs a generic instance of this exception using the specified reason.
*
* @param reason for the exception
*/
public UnexpectedServerException(final String reason) {
super(reason);
}
/**
* Constructs a instance of this exception with the specified reason and backing root
* exception.
*
* @param reason for this exception
* @param rootException causing this exception
*/
public UnexpectedServerException(final String reason, final Throwable rootException) {
super(reason, rootException);
}
}

View File

@ -1,4 +0,0 @@
/**
* Custom exceptions of the {@link hirs.attestationca.AttestationCertificateAuthority}.
*/
package hirs.attestationca.exceptions;

View File

@ -1,5 +0,0 @@
/**
* Base package that includes common exceptions, interfaces and base implementations for and related
* to the ACA.
*/
package hirs.attestationca;

View File

@ -1,72 +0,0 @@
package hirs.attestationca.rest;
import hirs.attestationca.AcaRestError;
import hirs.attestationca.exceptions.CertificateProcessingException;
import hirs.attestationca.exceptions.IdentityProcessingException;
import hirs.attestationca.exceptions.UnexpectedServerException;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
/**
* Handle processing of exceptions for ACA REST API.
*/
@ControllerAdvice
public class AttestationCertificateAuthorityExceptionHandler
extends ResponseEntityExceptionHandler {
private static final Logger LOGGER = LogManager.getLogger(
AttestationCertificateAuthorityExceptionHandler.class);
/**
* Method to handle errors of the type {@link CertificateProcessingException},
* {@link IdentityProcessingException}, and {@link IllegalArgumentException}
* that are thrown when performing a RESTful operation.
*
* @param ex exception that was thrown
* @param request the web request that started the RESTful operation
* @return the response entity that will form the message returned to the client
*/
@ExceptionHandler({ CertificateProcessingException.class, IdentityProcessingException.class,
IllegalArgumentException.class })
public final ResponseEntity<Object> handleExpectedExceptions(final Exception ex,
final WebRequest request) {
LOGGER.error(String.format("The ACA has encountered an expected exception: %s",
ex.getMessage()), ex);
return handleGeneralException(ex, HttpStatus.BAD_REQUEST, request);
}
/**
* Method to handle errors of the type {@link IllegalStateException} and
* {@link UnexpectedServerException} that are thrown when performing a RESTful operation.
*
* @param ex exception that was thrown
* @param request the web request that started the RESTful operation
* @return the response entity that will form the message returned to the client
*/
@ExceptionHandler({ IllegalStateException.class, UnexpectedServerException.class })
public final ResponseEntity<Object> handleUnexpectedExceptions(final Exception ex,
final WebRequest request) {
LOGGER.error(String.format("The ACA has encountered an unexpected exception: %s",
ex.getMessage()), ex);
return handleGeneralException(ex, HttpStatus.INTERNAL_SERVER_ERROR, request);
}
private ResponseEntity<Object> handleGeneralException(final Exception ex,
final HttpStatus responseStatus,
final WebRequest request) {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
return handleExceptionInternal(ex, new AcaRestError(ex.getMessage()),
headers, responseStatus, request);
}
}

View File

@ -1,130 +0,0 @@
package hirs.attestationca.rest;
import hirs.attestationca.AbstractAttestationCertificateAuthority;
import hirs.attestationca.service.SupplyChainValidationService;
import hirs.data.service.DeviceRegister;
import hirs.persist.CertificateManager;
import hirs.persist.DBManager;
import hirs.persist.DeviceManager;
import hirs.persist.ReferenceDigestManager;
import hirs.persist.ReferenceEventManager;
import hirs.persist.ReferenceManifestManager;
import hirs.persist.TPM2ProvisionerState;
import hirs.structs.converters.StructConverter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
/**
* Restful implementation of the {@link hirs.attestationca.AttestationCertificateAuthority}.
* Exposes the ACA methods as REST endpoints.
*/
@RestController
@RequestMapping("/")
public class RestfulAttestationCertificateAuthority
extends AbstractAttestationCertificateAuthority {
/**
* Constructor.
* @param supplyChainValidationService the supply chain service
* @param privateKey the ACA private key
* @param acaCertificate the ACA certificate
* @param structConverter the struct converter
* @param certificateManager the certificate manager
* @param referenceManifestManager the referenceManifestManager
* @param deviceRegister the device register
* @param validDays the number of days issued certs are valid
* @param deviceManager the device manager
* @param tpm2ProvisionerStateDBManager the DBManager for persisting provisioner state
* @param referenceDigestManager the reference digest manager
* @param referenceEventManager the reference event manager
*/
@SuppressWarnings({ "checkstyle:parameternumber" })
@Autowired
public RestfulAttestationCertificateAuthority(
final SupplyChainValidationService supplyChainValidationService,
final PrivateKey privateKey, final X509Certificate acaCertificate,
final StructConverter structConverter,
final CertificateManager certificateManager,
final ReferenceManifestManager referenceManifestManager,
final DeviceRegister deviceRegister,
final DeviceManager deviceManager,
final DBManager<TPM2ProvisionerState> tpm2ProvisionerStateDBManager,
final ReferenceDigestManager referenceDigestManager,
final ReferenceEventManager referenceEventManager,
@Value("${aca.certificates.validity}") final int validDays) {
super(supplyChainValidationService, privateKey, acaCertificate, structConverter,
certificateManager, referenceManifestManager,
deviceRegister, validDays, deviceManager,
tpm2ProvisionerStateDBManager, referenceDigestManager, referenceEventManager);
}
/*
* (non-javadoc)
*
* Wrap the {@link AbstractAttestationCertificateAuthority#processIdentityRequest(byte[])}
* with a Spring {@link RequestMapping}. Effectively, this method then will allow spring to
* serialize and deserialize the request and responses on method invocation and
* return, respectively.
*/
@Override
@ResponseBody
@RequestMapping(value = "/identity-request/process",
method = RequestMethod.POST,
consumes = MediaType.APPLICATION_OCTET_STREAM_VALUE)
public byte[] processIdentityRequest(@RequestBody final byte[] request) {
return super.processIdentityRequest(request);
}
/**
* Listener for identity requests from TPM 2.0 provisioning.
* @param request The request object from the provisioner.
* @return The response to the provisioner.
*/
@Override
@ResponseBody
@RequestMapping(value = "/identity-claim-tpm2/process",
method = RequestMethod.POST,
consumes = MediaType.APPLICATION_OCTET_STREAM_VALUE)
public byte[] processIdentityClaimTpm2(@RequestBody final byte[] request) {
return super.processIdentityClaimTpm2(request);
}
/**
* Endpoint for processing certificate requests for TPM 2.0 provisioning.
*
* @param request The credential request from the client provisioner.
* @return The response to the client provisioner.
*/
@Override
@ResponseBody
@RequestMapping(value = "/request-certificate-tpm2",
method = RequestMethod.POST,
consumes = MediaType.APPLICATION_OCTET_STREAM_VALUE)
public byte[] processCertificateRequest(@RequestBody final byte[] request) {
return super.processCertificateRequest(request);
}
/*
* (non-javadoc)
*
* Wrap the {@link AbstractAttestationCertificateAuthority#getPublicKey()} with a Spring
* {@link RequestMapping} such that Spring can serialize the certificate to be returned to an
* HTTP Request.
*/
@Override
@ResponseBody
@RequestMapping(value = "/public-key", method = RequestMethod.GET)
public byte[] getPublicKey() {
return super.getPublicKey();
}
}

View File

@ -1,4 +0,0 @@
/**
* RESTful implementations of the {@link hirs.attestationca.AttestationCertificateAuthority}.
*/
package hirs.attestationca.rest;

View File

@ -1,44 +0,0 @@
package hirs.attestationca.service;
import java.util.Set;
import hirs.data.persist.Device;
import hirs.data.persist.SupplyChainPolicy;
import hirs.data.persist.SupplyChainValidationSummary;
import hirs.data.persist.certificate.EndorsementCredential;
import hirs.data.persist.certificate.PlatformCredential;
/**
* Interface defining a component that will perform supply chain validations, which yields a
* {@link SupplyChainValidationSummary}.
*/
public interface SupplyChainValidationService {
/**
* The "main" method of supply chain validation. Takes the credentials from an identity
* request and validates the supply chain in accordance to the current supply chain
* policy.
*
* @param ec The endorsement credential from the identity request.
* @param pc The set of platform credentials from the identity request.
* @param device The device to be validated.
* @return True if validation is successful, false otherwise.
*/
SupplyChainValidationSummary validateSupplyChain(EndorsementCredential ec,
Set<PlatformCredential> pc,
Device device);
/**
* A supplemental method that handles validating just the quote post main validation.
*
* @param device the associated device.
* @return True if validation is successful, false otherwise.
*/
SupplyChainValidationSummary validateQuote(Device device);
/**
* Allows other service access to the policy information.
* @return supply chain policy
*/
SupplyChainPolicy getPolicy();
}

View File

@ -1,5 +0,0 @@
/**
* Contains the main functionality of the SupplyChainValidationService. Executes the actual
* validation based on the current supply chain policy.
*/
package hirs.attestationca.service;

View File

@ -1,12 +0,0 @@
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- Instructs Spring to scan the ACA packages for annotations -->
<context:component-scan base-package="hirs.attestationca"/>
</beans>

View File

@ -1,37 +0,0 @@
# General notes:
# Properties are processed using an expression processor. That said, properties can inherit the
# values of other properties.
#
# In example the processor will resolve the value of aca.both as 'hello world!'.
# aca.hello = hello
# aca.world = world!
# aca.both = ${aca.hello} ${aca.world}
#
# ACA Directories
# root: the root directory of ACA related files
# certificates: the directory for ACA certificate files
aca.directories.root = /etc/hirs/aca
aca.directories.certificates = ${aca.directories.root}/certificates
# ACA certificate related properties. These are generic properties that apply to the creation of
# any certificate that the ACA is responsible for creating.
# validity: the number of days that credentials generated by the ACA are valid.
aca.certificates.validity = 3652
# ACA key store properties
# alias: the alias to reference the ACA key and certificate by
# location: the absolute path to the ACA key store.
# password: key store password
aca.keyStore.alias = HIRS_ACA_KEY
aca.keyStore.location = ${aca.directories.certificates}/keyStore.jks
aca.keyStore.password =
# ACA setup/initialization properties. These properties are used exclusively by the ACA
# initialization process. Generally these properties do not need to be modified
#
# keySize: the default key size of the ACA key pair stored within the trust store
# subjectName: the CN of the generate X509 certificate
# expiration: the number of days that the generated X509 certificate will expire
aca.setup.keyStore.keySize = 2048
aca.setup.keyStore.subjectName = HIRS_AttestationCA_Endorsement
aca.setup.keyStore.expiration = ${aca.certificates.validity}

View File

@ -1,24 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
<Appenders>
<Console name="STDOUT" target="SYSTEM_OUT">
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} [%t @ %C.%M] %-5p : %m%n"/>
</Console>
<RollingFile name="FILE" fileName="logs/HIRS_AttestationCA.log"
filePattern="logs/HIRS_AttestationCA.log-%d{yyyy-MM-dd}-%i.log" >
<PatternLayout>
<pattern>%d{yyyy-MM-dd HH:mm:ss} [%C.%M] %-5p : %m%n</pattern>
</PatternLayout>
<Policies>
<SizeBasedTriggeringPolicy size="10 MB" />
</Policies>
<DefaultRolloverStrategy max="10"/>
</RollingFile>
</Appenders>
<Loggers>
<Root level = "WARN">
<AppenderRef ref="STDOUT" level="WARN" />
<AppenderRef ref="FILE"/>
</Root>
</Loggers>
</Configuration>

View File

@ -1,8 +0,0 @@
#used in the initial setup of the client-files properties files upon WAR deployment
attestationca.url=https://***replace***:8443/HIRS_AttestationCA
attestationca.cert.subjectname=HIRS_AttestationCA
attestationca.cert.password=***replace***
endorsementca.cert.subjectname=HIRS_AttestationCA_Endorsement
cert.validitydays = 3652
aik.auth=0000000000000000000000000000000000000000

View File

@ -1,43 +0,0 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5" metadata-complete="true">
<display-name>HIRS Attestation Certificate Authority</display-name>
<!-- Specify the location of the LOG4J file -->
<context-param>
<param-name>log4j.configurationFile</param-name>
<param-value>classpath:log4j2.xml</param-value>
</context-param>
<!-- Configure spring to not expose web app root system property -->
<context-param>
<param-name>log4jExposeWebAppRoot</param-name>
<param-value>false</param-value>
</context-param>
<!-- Configures the main Spring Servlet that dispatches HTTP requests to Controllers -->
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</init-param>
</servlet>
<!-- Map all HTTP requests to the dispatcher -->
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<listener>
<listener-class>hirs.attestationca.InitializationListener</listener-class>
</listener>
</web-app>

View File

@ -1,905 +0,0 @@
package hirs.attestationca;
import com.google.protobuf.ByteString;
import hirs.data.persist.certificate.PlatformCredential;
import hirs.structs.converters.StructConverter;
import hirs.structs.elements.aca.SymmetricAttestation;
import hirs.structs.elements.tpm.AsymmetricKeyParams;
import hirs.structs.elements.tpm.AsymmetricPublicKey;
import hirs.structs.elements.tpm.EncryptionScheme;
import hirs.structs.elements.tpm.IdentityProof;
import hirs.structs.elements.tpm.IdentityRequest;
import hirs.structs.elements.tpm.StorePubKey;
import hirs.structs.elements.tpm.SymmetricKey;
import hirs.structs.elements.tpm.SymmetricKeyParams;
import hirs.structs.elements.tpm.SymmetricSubParams;
import hirs.utils.HexUtils;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.lang3.ArrayUtils;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.asn1.x509.GeneralNames;
import org.bouncycastle.asn1.x509.TBSCertificate;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.springframework.test.util.ReflectionTestUtils;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.BeforeSuite;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.OAEPParameterSpec;
import javax.crypto.spec.PSource;
import javax.crypto.spec.SecretKeySpec;
import javax.security.auth.x500.X500Principal;
import java.io.IOException;
import java.math.BigInteger;
import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Security;
import java.security.cert.X509Certificate;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.MGF1ParameterSpec;
import java.util.Calendar;
import java.util.HashSet;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertTrue;
/**
* Test suite for {@link AbstractAttestationCertificateAuthority}.
*/
public class AbstractAttestationCertificateAuthorityTest {
// object in test
private AbstractAttestationCertificateAuthority aca;
private static final String EK_PUBLIC_PATH = "/tpm2/ek.pub";
private static final String AK_PUBLIC_PATH = "/tpm2/ak.pub";
private static final String AK_NAME_PATH = "/tpm2/ak.name";
private static final String TEST_NONCE_BLOB_PATH = "test/nonce.blob";
private static final String EK_MODULUS_HEX = "a3 b5 c2 1c 57 be 40 c4 3c 78 90 0d 00 81 01 78"
+ "13 ca 02 ec b6 75 89 60 ca 60 9b 10 b6 b4 d0 0b"
+ "4d e4 68 ad 01 a6 91 e2 56 20 5e cf 16 fe 77 ae"
+ "1f 13 d7 ac a1 91 0b 68 f6 07 cf c2 4b 5e c1 2c"
+ "4c fe 3a c9 62 7e 10 02 5b 33 c8 c2 1a cd 2e 7f"
+ "dd 7c 43 ac a9 5f b1 d6 07 56 4f 72 9b 0a 00 6c"
+ "f6 8d 23 a1 84 ca c1 7f 5a 8b ef 0e 23 11 90 00"
+ "30 f2 99 e9 94 59 c6 b0 fe b2 5c 0c c7 b4 76 69"
+ "6c f1 b7 d8 e5 60 d6 61 9f ab 7c 17 ce a4 74 6d"
+ "8c cd e6 9e 6e bb 64 52 a7 c3 bf ac 07 e8 5e 3e"
+ "ae eb dc c5 95 37 26 6a 5d a6 a2 12 52 fa 03 43"
+ "b2 62 2d 87 8c a7 06 8f d6 3f 63 b6 2d 73 c4 9d"
+ "9d d6 55 0e bb db b1 eb dd c5 4b 8f c3 17 cb 3b"
+ "c3 bf f6 7f 13 44 de 8e d7 b9 f1 a7 15 56 8f 6c"
+ "cd f2 4c 86 99 39 19 88 d3 4a 2f 38 c4 c4 37 39"
+ "85 6f 41 98 19 14 a4 1f 95 bc 04 ef 74 c2 0d f3";
private static final String AK_MODULUS_HEX = "d7 c9 f0 e3 ac 1b 4a 1e 3c 9d 2d 57 02 e9 2a 93"
+ "b0 c0 e1 50 af e4 61 11 31 73 a1 96 b8 d6 d2 1c"
+ "40 40 c8 a6 46 a4 10 4b d1 06 74 32 f6 e3 8a 55"
+ "1e 03 c0 3e cc 75 04 c6 44 88 b6 ad 18 c9 45 65"
+ "0d be c5 45 22 bd 24 ad 32 8c be 83 a8 9b 1b d9"
+ "e0 c8 d9 ec 14 67 55 1b fe 68 dd c7 f7 33 e4 cd"
+ "87 bd ba 9a 07 e7 74 eb 57 ef 80 9c 6d ee f9 35"
+ "52 67 36 e2 53 98 46 a5 4e 8f 17 41 8d ff eb bb"
+ "9c d2 b4 df 57 f8 7f 31 ef 2e 2d 6e 06 7f 05 ed"
+ "3f e9 6f aa b4 b7 5a f9 6d ba ff 2b 5e f7 c1 05"
+ "90 68 1f b6 4b 38 67 f7 92 d8 73 51 6e 08 19 ad"
+ "ca 35 48 a7 c1 fb cb 01 9a 28 03 c9 fe bb 49 2f"
+ "88 3f a1 e7 a8 69 f0 f8 e8 78 db d3 6d c5 80 8d"
+ "c2 e4 8a af 4b c2 ac 48 2a 44 63 6e 39 b0 8f dd"
+ "e4 b3 a3 f9 2a b1 c8 d9 3d 6b c4 08 b0 16 c4 e7"
+ "c7 2f f5 94 c6 43 3e ee 9b 8a da e7 31 d1 54 dd";
private static final String AK_NAME_HEX = "00 0b 6e 8f 79 1c 7e 16 96 1b 11 71 65 9c e0 cd"
+ "ae 0d 4d aa c5 41 be 58 89 74 67 55 96 c2 5e 38"
+ "e2 94";
// test key pair
private KeyPair keyPair;
/**
* Registers bouncy castle as a security provider. Normally the JEE container will handle this,
* but since the tests are not instantiating a container, have the unit test runner setup the
* provider.
*/
@BeforeClass
public static void setupTests() {
Security.addProvider(new BouncyCastleProvider());
}
/**
* Instantiates an anonymous instance of the {@link AbstractAttestationCertificateAuthority}.
* This is sufficient as this class does not have any unimplemented or abstract methods.
*/
@BeforeTest
public void setup() {
aca = new AbstractAttestationCertificateAuthority(null, keyPair.getPrivate(),
null, null, null, null, null, 1,
null, null, null, null) {
};
}
/**
* Generates a key pair that can be used by the test suite.
*
* @throws Exception during key generation
*/
@BeforeSuite
public void suiteSetup() throws Exception {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(2048);
keyPair = keyPairGenerator.generateKeyPair();
}
/**
* Tests {@link AbstractAttestationCertificateAuthority#processIdentityRequest(byte[])}
* where the byte array is null. Expects an illegal argument exception to be thrown.
*/
@Test(expectedExceptions = IllegalArgumentException.class)
public void testProcessIdentityRequestNullRequest() {
aca.processIdentityRequest(null);
}
/**
* Tests {@link AbstractAttestationCertificateAuthority#processIdentityClaimTpm2(byte[])}
* where the byte array is null. Expects an illegal argument exception to be thrown.
*/
@Test(expectedExceptions = IllegalArgumentException.class)
public void testProcessIdentityClaimTpm2NullRequest() {
aca.processIdentityClaimTpm2(null);
}
/**
* Tests {@link AbstractAttestationCertificateAuthority#getPublicKey()}.
*/
@Test
public void testGetPublicKey() {
// encoded byte array to be returned by public key
byte[] encoded = new byte[]{0, 1, 0, 1, 0};
// create mocks for testing
X509Certificate acaCertificate = mock(X509Certificate.class);
PublicKey publicKey = mock(PublicKey.class);
// assign the aca certificate to the aca
ReflectionTestUtils.setField(aca, "acaCertificate", acaCertificate);
// return a mocked public key
when(acaCertificate.getPublicKey()).thenReturn(publicKey);
// return test byte array
when(publicKey.getEncoded()).thenReturn(encoded);
// assert what the ACA returns is as expected
assertEquals(aca.getPublicKey(), encoded);
// verify mock interactions
verify(acaCertificate).getPublicKey();
verify(publicKey).getEncoded();
// verify no other interactions with mocks
verifyNoMoreInteractions(acaCertificate, publicKey);
}
/**
* Tests {@link AbstractAttestationCertificateAuthority#unwrapIdentityRequest(byte[])}.
*
* @throws Exception during aca processing
*/
@Test
public void testUnwrapIdentityRequest() throws Exception {
// create a key generator to generate a "shared" secret
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
keyGenerator.init(128);
// test variables
byte[] request = new byte[1];
byte[] iv = new byte[16];
byte[] asymmetricBlob = new byte[]{1, 2, 3};
byte[] symmetricBlob = new byte[]{3, 2, 1};
byte[] secretKey = keyGenerator.generateKey().getEncoded();
// fill the IV with random bytes
SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
random.nextBytes(iv);
// encrypt the asymmetric blob
byte[] encryptedAsymmetricBlob =
encryptBlob(asymmetricBlob, EncryptionScheme.OAEP.toString());
// encrypt the symmetric blob
byte[] encryptedSymmetricBlob =
encryptBlob(symmetricBlob, secretKey, iv, "AES/CBC/PKCS5Padding");
// create some mocks for the tests
IdentityRequest identityRequest = mock(IdentityRequest.class);
AsymmetricKeyParams asymmetricKeyParams = mock(AsymmetricKeyParams.class);
SymmetricKeyParams symmetricKeyParams = mock(SymmetricKeyParams.class);
SymmetricSubParams symmetricSubParams = mock(SymmetricSubParams.class);
StructConverter structConverter = mock(StructConverter.class);
SymmetricKey symmetricKey = mock(SymmetricKey.class);
// assign the mocked struct converter to the test object
ReflectionTestUtils.setField(aca, "structConverter", structConverter);
// when converting our test request byte array, return our mocked identity request
// when converting our test asymmetric blob to a symmetric key, return the mocked key
when(structConverter.convert(request, IdentityRequest.class)).thenReturn(identityRequest);
when(structConverter.convert(asymmetricBlob, SymmetricKey.class)).thenReturn(symmetricKey);
// mock out the identity request by returning other mocks as needed
when(identityRequest.getSymmetricAlgorithm()).thenReturn(symmetricKeyParams);
when(identityRequest.getAsymmetricAlgorithm()).thenReturn(asymmetricKeyParams);
when(identityRequest.getAsymmetricBlob()).thenReturn(encryptedAsymmetricBlob);
when(identityRequest.getSymmetricBlob()).thenReturn(encryptedSymmetricBlob);
// use OAEP encryption scheme when asked
when(asymmetricKeyParams.getEncryptionScheme()).thenReturn(
(short) EncryptionScheme.OAEP_VALUE);
// use the mocked sub params when asked
when(symmetricKeyParams.getParams()).thenReturn(symmetricSubParams);
// use the test IV when asked
when(symmetricSubParams.getIv()).thenReturn(iv);
// use the test secret key when asked
when(symmetricKey.getKey()).thenReturn(secretKey);
// perform test
byte[] unwrappedRequest = aca.unwrapIdentityRequest(request);
// verify test results
assertEquals(unwrappedRequest, symmetricBlob);
// verify mock interactions
verify(structConverter).convert(request, IdentityRequest.class);
verify(structConverter).convert(asymmetricBlob, SymmetricKey.class);
verify(identityRequest).getSymmetricAlgorithm();
verify(identityRequest).getAsymmetricAlgorithm();
verify(identityRequest).getAsymmetricBlob();
verify(identityRequest).getSymmetricBlob();
verify(asymmetricKeyParams).getEncryptionScheme();
verify(symmetricKeyParams, times(2)).getParams();
verify(symmetricSubParams).getIv();
verify(symmetricKey).getKey();
verifyNoMoreInteractions(identityRequest, symmetricKeyParams, symmetricSubParams,
asymmetricKeyParams, structConverter, symmetricKey);
}
/**
* Tests {@link AbstractAttestationCertificateAuthority#unwrapIdentityRequest(byte[])}.
*
* @throws Exception during aca processing
*/
@Test
public void testUnwrapIdentityRequestNoKeyParams() throws Exception {
// create a key generator to generate a "shared" secret
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
keyGenerator.init(128);
// test variables
byte[] request = new byte[1];
byte[] iv = new byte[16];
byte[] asymmetricBlob = new byte[]{1, 2, 3};
byte[] symmetricBlob = new byte[]{3, 2, 1};
byte[] secretKey = keyGenerator.generateKey().getEncoded();
// fill the IV with random bytes
SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
random.nextBytes(iv);
// encrypt the asymmetric blob
byte[] encryptedAsymmetricBlob =
encryptBlob(asymmetricBlob, EncryptionScheme.OAEP.toString());
// encrypt the symmetric blob
byte[] encryptedSymmetricBlob =
encryptBlob(symmetricBlob, secretKey, iv, "AES/CBC/PKCS5Padding");
// since there is no symmetric key params, put the IV in front of encrypted blob.
byte[] totalBlob = ArrayUtils.addAll(iv, encryptedSymmetricBlob);
// create some mocks for the tests
IdentityRequest identityRequest = mock(IdentityRequest.class);
AsymmetricKeyParams asymmetricKeyParams = mock(AsymmetricKeyParams.class);
StructConverter structConverter = mock(StructConverter.class);
SymmetricKey symmetricKey = mock(SymmetricKey.class);
// assign the mocked struct converter to the test object
ReflectionTestUtils.setField(aca, "structConverter", structConverter);
// when converting our test request byte array, return our mocked identity request
// when converting our test asymmetric blob to a symmetric key, return the mocked key
when(structConverter.convert(request, IdentityRequest.class)).thenReturn(identityRequest);
when(structConverter.convert(asymmetricBlob, SymmetricKey.class)).thenReturn(symmetricKey);
// mock out the identity request by returning other mocks as needed
when(identityRequest.getSymmetricAlgorithm()).thenReturn(null);
when(identityRequest.getAsymmetricAlgorithm()).thenReturn(asymmetricKeyParams);
when(identityRequest.getAsymmetricBlob()).thenReturn(encryptedAsymmetricBlob);
// the first request should return IV + encrypted blob. Subsequent requests should return
// just the encrypted portion of the blob
when(identityRequest.getSymmetricBlob()).thenReturn(totalBlob)
.thenReturn(encryptedSymmetricBlob);
// use OAEP encryption scheme when asked
when(asymmetricKeyParams.getEncryptionScheme()).thenReturn(
(short) EncryptionScheme.OAEP_VALUE);
// use the test secret key when asked
when(symmetricKey.getKey()).thenReturn(secretKey);
// perform test
byte[] unwrappedRequest = aca.unwrapIdentityRequest(request);
// verify test results
assertEquals(unwrappedRequest, symmetricBlob);
// verify mock interactions
verify(structConverter).convert(request, IdentityRequest.class);
verify(structConverter).convert(asymmetricBlob, SymmetricKey.class);
verify(identityRequest).getSymmetricAlgorithm();
verify(identityRequest).getAsymmetricAlgorithm();
verify(identityRequest).getAsymmetricBlob();
verify(identityRequest, times(2)).getSymmetricBlob();
verify(identityRequest).setSymmetricBlob(encryptedSymmetricBlob);
verify(asymmetricKeyParams).getEncryptionScheme();
verify(symmetricKey).getKey();
verifyNoMoreInteractions(identityRequest, asymmetricKeyParams, structConverter,
symmetricKey);
}
/**
* Tests {@link AbstractAttestationCertificateAuthority#decryptAsymmetricBlob(byte[],
* EncryptionScheme)}.
*
* @throws Exception during aca processing
*/
@Test
public void testDecryptAsymmetricBlob() throws Exception {
// test encryption transformation
EncryptionScheme encryptionScheme = EncryptionScheme.PKCS1;
// test variables
byte[] expected = "test".getBytes();
// encrypt the expected value using same algorithm as the ACA.
byte[] encrypted = encryptBlob(expected, encryptionScheme.toString());
// perform the decryption and assert that the decrypted bytes equal the expected bytes
assertEquals(aca.decryptAsymmetricBlob(encrypted, encryptionScheme), expected);
}
/**
* Tests {@link AbstractAttestationCertificateAuthority#decryptSymmetricBlob(
* byte[], byte[], byte[], String)}.
*
* @throws Exception during aca processing
*/
@Test
public void testDecryptSymmetricBlob() throws Exception {
// test encryption transformation
String transformation = "AES/CBC/PKCS5Padding";
// test variables
byte[] expected = "test".getBytes();
// create a key generator to generate a "shared" secret
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
keyGenerator.init(128);
// use some random bytes as the IV to encrypt and subsequently decrypt with
byte[] randomBytes = new byte[16];
// generate the random bytes
SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
random.nextBytes(randomBytes);
// the shared secret
byte[] secretKey = keyGenerator.generateKey().getEncoded();
// encrypt the expected value with the private key being the shared secret
byte[] encrypted = encryptBlob(expected, secretKey, randomBytes, transformation);
// perform the decryption using the generated shared secert, random bytes as an IV, and the
// AES CBC transformation for the cipher. then assert the decrypted results are the same
// as our expected value.
assertEquals(aca.decryptSymmetricBlob(encrypted, secretKey, randomBytes, transformation),
expected);
}
/**
* Tests {@link AbstractAttestationCertificateAuthority#generateSymmetricKey()}.
*/
@Test
public void testGenerateSymmetricKey() {
// perform the test
SymmetricKey symmetricKey = aca.generateSymmetricKey();
// assert the symmetric algorithm, scheme, and key size are all set appropriately
assertTrue(symmetricKey.getAlgorithmId() == 6);
assertTrue(symmetricKey.getEncryptionScheme() == 255);
assertTrue(symmetricKey.getKeySize() == symmetricKey.getKey().length);
}
/**
* Tests {@link AbstractAttestationCertificateAuthority#generateAsymmetricContents(
* IdentityProof, SymmetricKey, PublicKey)}.
*
* @throws Exception during aca processing
*/
@Test
public void testGenerateAsymmetricContents() throws Exception {
// mocks for test
IdentityProof proof = mock(IdentityProof.class);
AsymmetricPublicKey publicKey = mock(AsymmetricPublicKey.class);
StructConverter structConverter = mock(StructConverter.class);
SymmetricKey symmetricKey = mock(SymmetricKey.class);
// assign the mocked struct converter to the test object
ReflectionTestUtils.setField(aca, "structConverter", structConverter);
// "encoded" identity proof (returned by struct converter)
byte[] identityProofEncoded = new byte[]{0, 0, 1, 1};
// generate a random session key to be used for encryption and decryption
byte[] sessionKey = new byte[16];
SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
random.nextBytes(sessionKey);
// when requesting the identity key from the proof, return the mocked public key
when(proof.getIdentityKey()).thenReturn(publicKey);
// when requesting to convert the public key, return the encoded identity proof
when(structConverter.convert(publicKey)).thenReturn(identityProofEncoded);
when(structConverter.convert(symmetricKey)).thenReturn(sessionKey);
// perform the test
byte[] result = aca.generateAsymmetricContents(proof, symmetricKey, keyPair.getPublic());
// verify mock interactions
verify(proof).getIdentityKey();
verify(structConverter).convert(publicKey);
verify(structConverter).convert(symmetricKey);
verifyZeroInteractions(proof, structConverter, publicKey, symmetricKey);
// decrypt the result
byte[] decryptedResult = decryptBlob(result);
// create a SHA1 digest of the identity key
MessageDigest md = MessageDigest.getInstance("SHA-1");
md.update(identityProofEncoded);
// generate the digest
byte[] identityDigest = md.digest();
// the decrypted asymmetric contents should be the session key and a SHA-1 hash of the
// encoded identity proof.
byte[] expected = ArrayUtils.addAll(sessionKey, identityDigest);
// compare the two byte arrays
assertEquals(decryptedResult, expected);
}
/**
* Tests {@link AbstractAttestationCertificateAuthority#generateAttestation(X509Certificate,
* SymmetricKey)}.
*
* @throws Exception during aca processing
*/
@Test
public void testGenerateAttestation() throws Exception {
// create some mocks for the unit tests
X509Certificate certificate = mock(X509Certificate.class);
SymmetricKey symmetricKey = mock(SymmetricKey.class);
// create a key generator to generate a secret key
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
keyGenerator.init(128);
// obtain the key from the generator
byte[] secretKey = keyGenerator.generateKey().getEncoded();
// use our public key for encryption
when(symmetricKey.getKey()).thenReturn(secretKey);
// just use the existing public key for the credential
when(certificate.getEncoded()).thenReturn(keyPair.getPublic().getEncoded());
// perform the actual test
SymmetricAttestation attestation = aca.generateAttestation(certificate, symmetricKey);
// validate that the attestation is not null
assertNotNull(attestation);
// validate the attestation algorithm
assertNotNull(attestation.getAlgorithm());
assertTrue(attestation.getAlgorithm().getAlgorithmId() == 6);
assertTrue(attestation.getAlgorithm().getEncryptionScheme() == 0x1);
assertTrue(attestation.getAlgorithm().getSignatureScheme() == 0);
assertTrue(attestation.getAlgorithm().getParamsSize() == 0);
// validate the attestation credential
assertNotNull(attestation.getCredential());
// validate that the credential size is the size of the actual credential block
assertTrue(attestation.getCredential().length == attestation.getCredentialSize());
// create containers for the 2 parts of the credential
byte[] iv = new byte[16];
byte[] credential = new byte[attestation.getCredential().length - iv.length];
// siphon off the first 16 bytes for the IV
System.arraycopy(attestation.getCredential(), 0, iv, 0, iv.length);
// the rest is the actual encrypted credential
System.arraycopy(attestation.getCredential(), iv.length, credential, 0, credential.length);
// decrypt the credential
byte[] decrypted = decryptBlob(credential, secretKey, iv, "AES/CBC/PKCS5Padding");
// assert that the decrypted credential is our public key
assertEquals(keyPair.getPublic().getEncoded(), decrypted);
// verify that the mocks were interacted with appropriately
verify(symmetricKey).getKey();
verify(certificate).getEncoded();
verifyNoMoreInteractions(certificate, symmetricKey);
}
/**
* Tests {@link AbstractAttestationCertificateAuthority#
* AbstractAttestationCertificateAuthority(SupplyChainValidationService, PrivateKey,
* X509Certificate, StructConverter, CertificateManager, DeviceRegister, int,
* DeviceManager, DBManager)}.
*
* @throws Exception during subject alternative name checking if cert formatting is bad
*/
@Test
public void testGenerateCredential() throws Exception {
// test variables
final String identityProofLabelString = "label";
byte[] identityProofLabel = identityProofLabelString.getBytes();
byte[] modulus = ((RSAPublicKey) keyPair.getPublic()).getModulus().toByteArray();
X500Principal principal = new X500Principal("CN=TEST, OU=TEST, O=TEST, C=TEST");
int validDays = 1;
// create mocks for testing
IdentityProof identityProof = mock(IdentityProof.class);
AsymmetricPublicKey asymmetricPublicKey = mock(AsymmetricPublicKey.class);
StorePubKey storePubKey = mock(StorePubKey.class);
X509Certificate acaCertificate = mock(X509Certificate.class);
// assign ACA fields
ReflectionTestUtils.setField(aca, "validDays", validDays);
ReflectionTestUtils.setField(aca, "acaCertificate", acaCertificate);
// prepare identity proof interactions
when(identityProof.getLabel()).thenReturn(identityProofLabel);
// prepare other mocks
when(acaCertificate.getSubjectX500Principal()).thenReturn(principal);
when(acaCertificate.getIssuerX500Principal()).thenReturn(principal);
// perform the test
X509Certificate certificate = aca.generateCredential(keyPair.getPublic(),
null,
new HashSet<PlatformCredential>(),
"exampleIdLabel");
// grab the modulus from the generate certificate
byte[] resultMod = ((RSAPublicKey) certificate.getPublicKey()).getModulus().toByteArray();
// today and tomorrow, when the certificate should be valid for
Calendar today = Calendar.getInstance();
Calendar tomorrow = Calendar.getInstance();
tomorrow.add(Calendar.DATE, 1);
// validate the certificate
assertTrue(certificate.getIssuerX500Principal().toString().contains("CN=TEST"));
assertTrue(certificate.getIssuerX500Principal().toString().contains("OU=TEST"));
assertTrue(certificate.getIssuerX500Principal().toString().contains("O=TEST"));
assertTrue(certificate.getIssuerX500Principal().toString().contains("C=TEST"));
// validate the format of the subject and subject alternative name
assertEquals(certificate.getSubjectX500Principal().getName(), "");
assertEquals(((X500Name) GeneralNames.fromExtensions(((TBSCertificate.getInstance(
certificate.getTBSCertificate()).getExtensions())), Extension.
subjectAlternativeName).getNames()[0].getName()).getRDNs(
IssuedCertificateAttributeHelper.TCPA_AT_TPM_ID_LABEL)[0].getFirst()
.getValue().toString(), "exampleIdLabel");
assertEquals(resultMod, modulus);
// obtain the expiration dates from the certificate
Calendar beforeDate = Calendar.getInstance();
Calendar afterDate = Calendar.getInstance();
beforeDate.setTime(certificate.getNotBefore());
afterDate.setTime(certificate.getNotAfter());
// assert the dates are set correctly
assertEquals(beforeDate.get(Calendar.DATE), today.get(Calendar.DATE));
assertEquals(afterDate.get(Calendar.DATE), tomorrow.get(Calendar.DATE));
// validate mock interactions
verify(acaCertificate).getSubjectX500Principal();
verifyNoMoreInteractions(identityProof, asymmetricPublicKey, storePubKey, acaCertificate);
}
/**
* Tests {@link AbstractAttestationCertificateAuthority#assemblePublicKey(byte[])}.
*/
@Test
public void testAssemblePublicKeyUsingByteArray() {
// obtain the expected modulus from the existing public key
final BigInteger modulus = ((RSAPublicKey) keyPair.getPublic()).getModulus();
// perform test
RSAPublicKey publicKey = (RSAPublicKey) aca.assemblePublicKey(modulus.toByteArray());
// assert that the exponent and the modulus are the same. the exponents should be the well
// known prime, 101
assertTrue(publicKey.getPublicExponent().equals(new BigInteger("010001", 16)));
assertTrue(publicKey.getModulus().equals(modulus));
}
/**
* Tests {@link AbstractAttestationCertificateAuthority#assemblePublicKey(String)}.
*/
@Test
public void testAssemblePublicKeyUsingHexEncodedString() {
// obtain the expected modulus from the existing public key
final BigInteger modulus = ((RSAPublicKey) keyPair.getPublic()).getModulus();
// encode our existing public key into hex
final String modulusString = Hex.encodeHexString(
((RSAPublicKey) keyPair.getPublic()).getModulus().toByteArray());
// perform test
RSAPublicKey publicKey = (RSAPublicKey) aca.assemblePublicKey(modulusString);
// assert that the exponent and the modulus are the same. the exponents should be the well
// known prime, 101.
assertTrue(publicKey.getPublicExponent().equals(new BigInteger("010001", 16)));
assertTrue(publicKey.getModulus().equals(modulus));
}
/**
* Tests parsing the EK from the TPM2 output file.
* @throws URISyntaxException incorrect resource path
* @throws IOException unable to read from file
*/
@Test
public void testParseEk() throws URISyntaxException, IOException {
Path ekPath = Paths.get(getClass().getResource(
EK_PUBLIC_PATH).toURI());
byte[] ekFile = Files.readAllBytes(ekPath);
RSAPublicKey ek = aca.parsePublicKey(ekFile);
assertTrue(ek.getPublicExponent().equals(new BigInteger("010001", 16)));
byte[] mod = ek.getModulus().toByteArray();
// big integer conversion is signed so it can add a 0 byte
if (mod[0] == 0) {
byte[] tmp = new byte[mod.length - 1];
System.arraycopy(mod, 1, tmp, 0, mod.length - 1);
mod = tmp;
}
String hex = HexUtils.byteArrayToHexString(mod);
String realMod = EK_MODULUS_HEX.replaceAll("\\s+", "");
assertEquals(hex, realMod);
}
/**
* Tests parsing the AK public key from the TPM2 output file.
* @throws URISyntaxException incorrect resource path
* @throws IOException unable to read from file
*/
@Test
public void testParseAk() throws URISyntaxException, IOException {
Path akPath = Paths.get(getClass().getResource(
AK_PUBLIC_PATH).toURI());
byte[] akFile = Files.readAllBytes(akPath);
RSAPublicKey ak = aca.parsePublicKey(akFile);
assertTrue(ak.getPublicExponent().equals(new BigInteger("010001", 16)));
byte[] mod = ak.getModulus().toByteArray();
// big integer conversion is signed so it can add a 0 byte
if (mod[0] == 0) {
byte[] tmp = new byte[mod.length - 1];
System.arraycopy(mod, 1, tmp, 0, mod.length - 1);
mod = tmp;
}
String hex = HexUtils.byteArrayToHexString(mod);
String realMod = AK_MODULUS_HEX.replaceAll("\\s+", "");
assertEquals(hex, realMod);
}
/**
* Tests parsing the AK name from the TPM2 output file.
* @throws URISyntaxException incorrect resource path
* @throws IOException unable to read from file
* @throws NoSuchAlgorithmException inavlid algorithm
*/
@Test
public void testGenerateAkName() throws URISyntaxException, IOException,
NoSuchAlgorithmException {
Path akNamePath = Paths.get(getClass().getResource(
AK_NAME_PATH).toURI());
byte[] akNameFileBytes = Files.readAllBytes(akNamePath);
String realHex = HexUtils.byteArrayToHexString(akNameFileBytes);
String realMod = AK_MODULUS_HEX.replaceAll("\\s+", "");
byte[] akName = aca.generateAkName(HexUtils.hexStringToByteArray(realMod));
String hex = HexUtils.byteArrayToHexString(akName);
String realName = AK_NAME_HEX.replaceAll("\\s+", "");
assertEquals(hex, realName);
assertEquals(hex, realHex);
}
/**
* Method to generate a make credential output file for use in manual testing. Feed to
* a TPM 2.0 or emulator using the activate credential command to ensure proper parsing.
* Must be performed manually. To use, copy the TPM's ek and ak into
* HIRS_AttestationCA/src/test/resources/tpm2/test/ and ensure the variables akPubPath
* and ekPubPath are correct. Your output file will be
* HIRS_AttestationCA/src/test/resources/tpm2/test/make.blob and the nonce used will be
* output as HIRS_AttestationCA/src/test/resources/tpm2/test/secret.blob
* @throws URISyntaxException invalid file path
* @throws IOException unable to read file
*/
@Test(enabled = false)
public void testMakeCredential() throws URISyntaxException, IOException {
Path akPubPath = Paths.get(getClass().getResource(
AK_PUBLIC_PATH).toURI());
Path ekPubPath = Paths.get(getClass().getResource(
EK_PUBLIC_PATH).toURI());
byte[] ekPubFile = Files.readAllBytes(ekPubPath);
byte[] akPubFile = Files.readAllBytes(akPubPath);
RSAPublicKey ekPub = aca.parsePublicKey(ekPubFile);
RSAPublicKey akPub = aca.parsePublicKey(akPubFile);
// prepare the nonce and wrap it with keys
byte[] nonce = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31};
ByteString blob = aca.tpm20MakeCredential(ekPub, akPub, nonce);
Path resources = Paths.get(getClass().getResource(
"/").toURI()).getParent().getParent().getParent().getParent();
Path makeBlob = resources.resolve("src/test/resources/tpm2/test/make.blob");
Files.write(makeBlob, blob.toByteArray());
Path secretPath = resources.resolve("src/test/resources/tpm2/test/secret.blob");
Files.write(secretPath, nonce);
}
/**
* Test helper method that encrypts a blob using the specified transformation and the test key
* pair public key.
*
* @param blob to be encrypted
* @param transformation used by a cipher to encrypt
* @return encrypted blob
* @throws Exception during the encryption process
*/
private byte[] encryptBlob(byte[] blob, String transformation) throws Exception {
// initialize a cipher using the specified transformation
Cipher cipher = Cipher.getInstance(transformation);
// use our generated public key to encrypt
cipher.init(Cipher.ENCRYPT_MODE, keyPair.getPublic());
// return the cipher text
return cipher.doFinal(blob);
}
/**
* Test helper method that encrypts a blob using a shared key and IV using the specified
* transformation.
*
* @param blob to be encrypted
* @param key shared key
* @param iv to encrypt with
* @param transformation of the encryption cipher
* @return encrypted blob
* @throws Exception
*/
private byte[] encryptBlob(byte[] blob, byte[] key, byte[] iv, String transformation)
throws Exception {
// initialize a cipher using the specified transformation
Cipher cipher = Cipher.getInstance(transformation);
// generate a secret key specification using the key and AES.
SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
// create IV parameter for key specification
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
// encrypt using the key specification with the generated IV
cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivParameterSpec);
// return the cipher text
return cipher.doFinal(blob);
}
/**
* Test helper method to decrypt blobs.
*
* @param blob to be decrypted
* @return decrypted blob
* @throws Exception
*/
private byte[] decryptBlob(byte[] blob) throws Exception {
// initialize a cipher using the specified transformation
Cipher cipher = Cipher.getInstance(EncryptionScheme.OAEP.toString());
OAEPParameterSpec spec = new OAEPParameterSpec("Sha1", "MGF1",
MGF1ParameterSpec.SHA1, new PSource.PSpecified("TCPA".getBytes()));
// use our generated public key to encrypt
cipher.init(Cipher.PRIVATE_KEY, keyPair.getPrivate(), spec);
// return the cipher text
return cipher.doFinal(blob);
}
/**
* Test helper method that decrypts a blob using a shared key and IV using the specified.
* transformation.
*
* @param blob to be decrypted
* @param key shared key
* @param iv to decrypt with
* @param transformation of the decryption cipher
* @return decrypted blob
* @throws Exception
*/
private byte[] decryptBlob(byte[] blob, byte[] key, byte[] iv, String transformation)
throws Exception {
// initialize a cipher using the specified transformation
Cipher cipher = Cipher.getInstance(transformation);
// generate a secret key specification using the key and AES.
SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
// create IV parameter for key specification
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
// encrypt using the key specification with the generated IV
cipher.init(Cipher.DECRYPT_MODE, keySpec, ivParameterSpec);
// return the cipher text
return cipher.doFinal(blob);
}
}

View File

@ -1,104 +0,0 @@
package hirs.attestationca;
import org.apache.commons.io.IOUtils;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import java.io.FileInputStream;
import java.io.IOException;
import hirs.data.persist.certificate.Certificate;
import hirs.persist.CertificateManager;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
/**
* Unit tests for {@see CredentialManagementHelper}.
*/
public class CredentialManagementHelperTest {
private CertificateManager certMan;
private static final String EK_HEADER_TRUNCATED
= "/certificates/nuc-1/ek_cert_7_byte_header_removed.cer";
private static final String EK_UNTOUCHED
= "/certificates/nuc-1/ek_cert_untouched.cer";
/**
* Setup mocks.
*/
@BeforeMethod
public void setUp() {
certMan = mock(CertificateManager.class);
}
/**
* Tests exception generated if providing a null cert manager.
*/
@Test(expectedExceptions = IllegalArgumentException.class)
public void processNullCertMan() {
CredentialManagementHelper.storeEndorsementCredential(null, null);
}
/**
* Tests exception generated when providing a null EK byte array.
*/
@Test(expectedExceptions = IllegalArgumentException.class)
public void processNullEndorsementCredential() {
CredentialManagementHelper.storeEndorsementCredential(certMan, null);
}
/**
* Tests exception generated when providing an empty array of bytes as the EK.
*/
@Test(expectedExceptions = IllegalArgumentException.class)
public void processEmptyEndorsementCredential() {
CredentialManagementHelper.storeEndorsementCredential(certMan, new byte[0]);
}
/**
* Tests processing an invalid EK (too small of an array).
*/
@Test(expectedExceptions = IllegalArgumentException.class)
public void processInvalidEndorsementCredentialCase1() {
byte[] ekBytes = new byte[] {1};
CredentialManagementHelper.storeEndorsementCredential(certMan, ekBytes);
}
/**
* Tests processing an invalid EK (garbage bytes of a reasonable length).
*/
@Test(expectedExceptions = IllegalArgumentException.class)
public void processInvalidEndorsementCredentialCase2() {
byte[] ekBytes = new byte[] {1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0};
CredentialManagementHelper.storeEndorsementCredential(certMan, ekBytes);
}
/**
* Tests processing a valid EK with the 7 byte header in tact.
* @throws IOException if an IO error occurs
*/
@Test
public void parseUntouchedEndorsementCredential() throws IOException {
String path = CredentialManagementHelperTest.class.getResource(EK_UNTOUCHED).getPath();
byte[] ekBytes = IOUtils.toByteArray(new FileInputStream(path));
CredentialManagementHelper.storeEndorsementCredential(certMan, ekBytes);
verify(certMan).save(any(Certificate.class));
}
/**
* Tests processing a valid EK with the 7 byte header already stripped.
* @throws IOException if an IO error occurs
*/
@Test
public void parseHeaderTruncatedEndorsementCredential() throws IOException {
String path = CredentialManagementHelperTest.class.getResource(EK_HEADER_TRUNCATED)
.getPath();
byte[] ekBytes = IOUtils.toByteArray(new FileInputStream(path));
CredentialManagementHelper.storeEndorsementCredential(certMan, ekBytes);
verify(certMan).save(any(Certificate.class));
}
}

View File

@ -1,204 +0,0 @@
package hirs.attestationca;
import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.asn1.DERSet;
import org.bouncycastle.asn1.DERTaggedObject;
import org.bouncycastle.asn1.DLSequence;
import org.bouncycastle.asn1.x509.Extension;
import org.testng.annotations.Test;
import java.io.IOException;
import java.net.URISyntaxException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import hirs.data.persist.certificate.EndorsementCredential;
import hirs.data.persist.certificate.PlatformCredential;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNull;
/**
* Tests for {@see IssuedCertificateAttributeHelper}.
*/
public class IssuedCertificateAttributeHelperTest {
private static final String NUC1_EC = "/certificates/nuc-1/tpmcert.pem";
private static final String INTEL_PC = "/certificates/platform_certs_2/"
+ "Intel_pc.pem";
private static final String TEST_HOSTNAME = "box1";
private static final String TPM_MANUFACTURER = "2.23.133.2.1";
private static final String TPM_MODEL = "2.23.133.2.2";
private static final String TPM_VERSION = "2.23.133.2.3";
private static final String TPM_ID_LABEL_OID = "2.23.133.2.15";
private static final String PLATFORM_MANUFACTURER = "2.23.133.2.4";
private static final String PLATFORM_MODEL = "2.23.133.2.5";
private static final String PLATFORM_VERSION = "2.23.133.2.6";
/**
* Test that provide a null host name and is rejected.
* @throws IOException an IO error occurs
*/
@Test(expectedExceptions = IllegalArgumentException.class)
public void rejectNullHostName() throws IOException {
IssuedCertificateAttributeHelper.buildSubjectAlternativeNameFromCerts(null, null, "");
}
/**
* Test that subject alt name can be built without an EC or PC.
* @throws IOException an IO error occurs
*/
@Test
public void buildAttributesNoEndorsementNoPlatform() throws IOException {
Extension subjectAlternativeName =
IssuedCertificateAttributeHelper.buildSubjectAlternativeNameFromCerts(
null, new ArrayList<PlatformCredential>(), TEST_HOSTNAME);
Map<String, String> subjectAlternativeNameAttrMap = getSubjectAlternativeNameAttributes(
subjectAlternativeName);
assertNull(subjectAlternativeNameAttrMap.get(TPM_MANUFACTURER));
assertNull(subjectAlternativeNameAttrMap.get(TPM_MODEL));
assertNull(subjectAlternativeNameAttrMap.get(TPM_VERSION));
assertNull(subjectAlternativeNameAttrMap.get(PLATFORM_MANUFACTURER));
assertNull(subjectAlternativeNameAttrMap.get(PLATFORM_MODEL));
assertNull(subjectAlternativeNameAttrMap.get(PLATFORM_VERSION));
assertEquals(subjectAlternativeNameAttrMap.get(TPM_ID_LABEL_OID), TEST_HOSTNAME);
}
/**
* Test that subject alt name can be built with an EC but no PC.
* @throws IOException an IO error occurs
* @throws URISyntaxException unrecognized URI for EC Path
*/
@Test
public void buildAttributesEndorsementNoPlatform() throws IOException, URISyntaxException {
Path endorsementCredentialPath = Paths.get(getClass().getResource(
NUC1_EC).toURI());
EndorsementCredential endorsementCredential = new EndorsementCredential(
endorsementCredentialPath);
Extension subjectAlternativeName =
IssuedCertificateAttributeHelper.buildSubjectAlternativeNameFromCerts(
endorsementCredential, new ArrayList<PlatformCredential>(), TEST_HOSTNAME);
Map<String, String> subjectAlternativeNameAttrMap = getSubjectAlternativeNameAttributes(
subjectAlternativeName);
assertEquals(subjectAlternativeNameAttrMap.get(TPM_MANUFACTURER),
endorsementCredential.getManufacturer());
assertEquals(subjectAlternativeNameAttrMap.get(TPM_MODEL),
endorsementCredential.getModel());
assertEquals(subjectAlternativeNameAttrMap.get(TPM_VERSION),
endorsementCredential.getVersion());
assertNull(subjectAlternativeNameAttrMap.get(PLATFORM_MANUFACTURER));
assertNull(subjectAlternativeNameAttrMap.get(PLATFORM_MODEL));
assertNull(subjectAlternativeNameAttrMap.get(PLATFORM_VERSION));
assertEquals(subjectAlternativeNameAttrMap.get(TPM_ID_LABEL_OID),
TEST_HOSTNAME);
}
/**
* Test that subject alt name can be built with an PC but no EC.
* @throws IOException an IO error occurs
* @throws URISyntaxException unrecognized URI for PC Path
*/
@Test
public void buildAttributesPlatformNoEndorsement() throws IOException, URISyntaxException {
Path platformCredentialPath = Paths.get(getClass().getResource(
INTEL_PC).toURI());
PlatformCredential platformCredential = new PlatformCredential(
platformCredentialPath);
List<PlatformCredential> platformCredentialList = new ArrayList<>();
platformCredentialList.add(platformCredential);
Extension subjectAlternativeName =
IssuedCertificateAttributeHelper.buildSubjectAlternativeNameFromCerts(
null, platformCredentialList, TEST_HOSTNAME);
Map<String, String> subjectAlternativeNameAttrMap = getSubjectAlternativeNameAttributes(
subjectAlternativeName);
assertNull(subjectAlternativeNameAttrMap.get(TPM_MANUFACTURER));
assertNull(subjectAlternativeNameAttrMap.get(TPM_MODEL));
assertNull(subjectAlternativeNameAttrMap.get(TPM_VERSION));
assertEquals(subjectAlternativeNameAttrMap.get(PLATFORM_MANUFACTURER),
platformCredential.getManufacturer());
assertEquals(subjectAlternativeNameAttrMap.get(PLATFORM_MODEL),
platformCredential.getModel());
assertEquals(subjectAlternativeNameAttrMap.get(PLATFORM_VERSION),
platformCredential.getVersion());
assertEquals(subjectAlternativeNameAttrMap.get(TPM_ID_LABEL_OID),
TEST_HOSTNAME);
}
/**
* Test that subject alt name can be built with a PC and an EC.
* @throws IOException an IO error occurs
* @throws URISyntaxException unrecognized URI for EC or PC Path
*/
@Test
public void buildAttributesPlatformAndEndorsement() throws IOException, URISyntaxException {
Path endorsementCredentialPath = Paths.get(getClass().getResource(
NUC1_EC).toURI());
Path platformCredentialPath = Paths.get(getClass().getResource(
INTEL_PC).toURI());
EndorsementCredential endorsementCredential = new EndorsementCredential(
endorsementCredentialPath);
PlatformCredential platformCredential = new PlatformCredential(
platformCredentialPath);
List<PlatformCredential> platformCredentialList = new ArrayList<>();
platformCredentialList.add(platformCredential);
Extension subjectAlternativeName =
IssuedCertificateAttributeHelper.buildSubjectAlternativeNameFromCerts(
endorsementCredential, platformCredentialList, TEST_HOSTNAME);
Map<String, String> subjectAlternativeNameAttrMap = getSubjectAlternativeNameAttributes(
subjectAlternativeName);
assertEquals(subjectAlternativeNameAttrMap.get(TPM_MANUFACTURER),
endorsementCredential.getManufacturer());
assertEquals(subjectAlternativeNameAttrMap.get(TPM_MODEL),
endorsementCredential.getModel());
assertEquals(subjectAlternativeNameAttrMap.get(TPM_VERSION),
endorsementCredential.getVersion());
assertEquals(subjectAlternativeNameAttrMap.get(PLATFORM_MANUFACTURER),
platformCredential.getManufacturer());
assertEquals(subjectAlternativeNameAttrMap.get(PLATFORM_MODEL),
platformCredential.getModel());
assertEquals(subjectAlternativeNameAttrMap.get(PLATFORM_VERSION),
platformCredential.getVersion());
assertEquals(subjectAlternativeNameAttrMap.get(TPM_ID_LABEL_OID),
TEST_HOSTNAME);
}
private Map<String, String> getSubjectAlternativeNameAttributes(
Extension subjectAlternativeName) {
Map<String, String> subjectAlternativeNameAttrMap = new HashMap<>();
DLSequence dlSequence = (DLSequence) subjectAlternativeName.getParsedValue();
DERTaggedObject derTaggedObject = (DERTaggedObject) dlSequence.getObjectAt(0);
DERSequence derSequence = (DERSequence) derTaggedObject.getObject();
Enumeration enumeration = derSequence.getObjects();
while (enumeration.hasMoreElements()) {
DERSet set = (DERSet) enumeration.nextElement();
DERSequence innerDerSequence = (DERSequence) set.getObjectAt(0);
subjectAlternativeNameAttrMap.put(innerDerSequence.getObjectAt(0).toString(),
innerDerSequence.getObjectAt(1).toString());
}
return subjectAlternativeNameAttrMap;
}
}

View File

@ -1,828 +0,0 @@
package hirs.attestationca.service;
import hirs.appraiser.SupplyChainAppraiser;
import hirs.data.persist.AppraisalStatus;
import hirs.data.persist.Device;
import hirs.data.persist.DeviceGroup;
import hirs.data.persist.DeviceInfoReport;
import hirs.data.persist.SpringPersistenceTest;
import hirs.data.persist.SupplyChainPolicy;
import hirs.data.persist.SupplyChainValidation;
import hirs.data.persist.SupplyChainValidationSummary;
import hirs.data.persist.certificate.Certificate;
import hirs.data.persist.certificate.CertificateAuthorityCredential;
import hirs.data.persist.certificate.DeviceAssociatedCertificate;
import hirs.data.persist.certificate.EndorsementCredential;
import hirs.data.persist.certificate.PlatformCredential;
import hirs.persist.AppraiserManager;
import hirs.persist.CertificateManager;
import hirs.persist.CrudManager;
import hirs.persist.DBCertificateManager;
import hirs.persist.DBDeviceGroupManager;
import hirs.persist.DBDeviceManager;
import hirs.persist.DeviceGroupManager;
import hirs.persist.DeviceManager;
import hirs.persist.PolicyManager;
import hirs.persist.ReferenceDigestManager;
import hirs.persist.ReferenceEventManager;
import hirs.validation.CredentialValidator;
import hirs.validation.SupplyChainCredentialValidator;
import org.mockito.ArgumentCaptor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.testng.Assert;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import java.io.IOException;
import java.math.BigInteger;
import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.cert.X509Certificate;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import static hirs.data.persist.AppraisalStatus.Status.FAIL;
import static hirs.data.persist.AppraisalStatus.Status.PASS;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyMapOf;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
/**
* Tests for the {@see SupplyChainValidationServiceImpl}.
*/
public class SupplyChainValidationServiceImplTest extends SpringPersistenceTest {
private static final String NUC1_EC = "/certificates/nuc-1/tpmcert.pem";
private static final String STM_ROOT_CA = "/certificates/stMicroCaCerts/stmtpmekroot.crt";
private static final String GS_ROOT_CA = "/certificates/stMicroCaCerts/gstpmroot.crt";
private static final String INTEL_CA = "/certificates/IntelSigningKey_20April2017.pem";
private static final String NUC_PC = "/certificates/platform_certs_2/Intel_pc.pem";
private static final String STM_TPM_EK_INTERMEDIATE_CA_02 =
"/certificates/STM TPM EK Intermediate CA.CER";
private static final String NUC_EC = "/certificates/nuc_ec.pem";
@Mock
private PolicyManager policyManager;
@Mock
private AppraiserManager appraiserManager;
@Mock
private CertificateManager certificateManager;
@Mock
private CredentialValidator supplyChainCredentialValidator;
@Mock
private CrudManager<SupplyChainValidationSummary> supplyChainValidationSummaryDBManager;
@Mock
private ReferenceDigestManager referenceDigestManager;
@Mock
private ReferenceEventManager referenceEventManager;
@InjectMocks
private SupplyChainValidationServiceImpl service;
// mocked
private SupplyChainPolicy policy;
private PlatformCredential pc;
private PlatformCredential delta;
private EndorsementCredential ec;
private HashSet<PlatformCredential> pcs;
private Device device;
/**
* Sets up the mocks.
*
@throws IOException won't actually throw, the method is being mocked instead of actually
* called
*/
@BeforeMethod
public void beforeClass() throws IOException {
MockitoAnnotations.initMocks(this);
device = mock(Device.class);
SupplyChainAppraiser appraiser = mock(SupplyChainAppraiser.class);
policy = mock(SupplyChainPolicy.class);
when(appraiserManager.getAppraiser(SupplyChainAppraiser.NAME)).thenReturn(appraiser);
when(policyManager.getDefaultPolicy(appraiser)).thenReturn(policy);
// mock endorsement credential
ec = mock(EndorsementCredential.class);
when(ec.getEncodedPublicKey()).thenReturn(new byte[] {0x0});
when(ec.getIssuerSorted()).thenReturn("STMicroelectronics NV");
Set<Certificate> resultEcs = new HashSet<>();
resultEcs.add(ec);
// mock platform credential
X509Certificate cert = mock(X509Certificate.class);
pc = mock(PlatformCredential.class);
when(pc.getId()).thenReturn(UUID.randomUUID());
when(pc.getX509Certificate()).thenReturn(cert);
when(pc.getSerialNumber()).thenReturn(BigInteger.ONE);
when(pc.getPlatformSerial()).thenReturn(String.valueOf(Integer.MIN_VALUE));
when(pc.getIssuerSorted()).thenReturn("STMicroelectronics NV");
when(pc.isBase()).thenReturn(true);
when(pc.getBeginValidity()).thenReturn(new Date(System.currentTimeMillis()));
when(pc.getSubjectSorted()).thenReturn("STMicroelectronics NV");
pcs = new HashSet<PlatformCredential>();
pcs.add(pc);
//Mock delta platform credential
X509Certificate deltaCert = mock(X509Certificate.class);
delta = mock(PlatformCredential.class);
when(delta.getId()).thenReturn(UUID.randomUUID());
when(delta.getX509Certificate()).thenReturn(deltaCert);
when(delta.getSerialNumber()).thenReturn(BigInteger.valueOf(2));
when(delta.getPlatformSerial()).thenReturn(String.valueOf(Integer.MIN_VALUE));
when(delta.getIssuerSorted()).thenReturn("STMicroelectronics NV");
when(delta.isBase()).thenReturn(false);
when(delta.getBeginValidity()).thenReturn(new Date(System.currentTimeMillis() + 1));
when(delta.getSubjectSorted()).thenReturn("STMicroelectronics NV Delta");
pcs.add(delta);
Set<Certificate> resultPcs = new HashSet<>();
resultPcs.add(pc);
resultPcs.add(delta);
// mock credential retrieval
when(certificateManager.get(any(EndorsementCredential.Selector.class)))
.thenReturn(resultEcs);
when(certificateManager.get(any(PlatformCredential.Selector.class)))
.thenReturn(resultPcs);
when(certificateManager.get(any(CertificateAuthorityCredential.Selector.class)))
.thenReturn(Collections.emptySet());
}
/**
* Remove test certificates and close the session factory.
*/
@AfterMethod
public void teardown() {
DBCertificateManager certMan = new DBCertificateManager(sessionFactory);
DBDeviceManager deviceMan = new DBDeviceManager(sessionFactory);
DBDeviceGroupManager groupMan = new DBDeviceGroupManager(sessionFactory);
certMan.deleteAll();
deviceMan.deleteAll();
groupMan.deleteAll();
}
/**
* All validations enabled, all pass.
*/
@Test
public final void testFullSuccessfulValidation() {
when(policy.isEcValidationEnabled()).thenReturn(true);
when(policy.isPcValidationEnabled()).thenReturn(true);
when(policy.isPcAttributeValidationEnabled()).thenReturn(true);
when(policy.isExpiredCertificateValidationEnabled()).thenReturn(true);
doReturn(new AppraisalStatus(PASS, "")).when(supplyChainCredentialValidator).
validateEndorsementCredential(eq(ec), any(KeyStore.class), eq(true));
doReturn(new AppraisalStatus(PASS, "")).when(supplyChainCredentialValidator)
.validatePlatformCredential(eq(pc), any(KeyStore.class), eq(true));
doReturn(new AppraisalStatus(PASS, "")).when(supplyChainCredentialValidator)
.validatePlatformCredential(eq(delta), any(KeyStore.class), eq(true));
doReturn(new AppraisalStatus(PASS, "")).when(supplyChainCredentialValidator)
.validatePlatformCredentialAttributes(eq(pc), any(DeviceInfoReport.class),
any(EndorsementCredential.class));
doReturn(new AppraisalStatus(PASS, "")).when(supplyChainCredentialValidator)
.validateDeltaPlatformCredentialAttributes(eq(delta), any(DeviceInfoReport.class),
eq(pc), anyMapOf(PlatformCredential.class, SupplyChainValidation.class));
Assert.assertEquals(service.validateSupplyChain(ec, pcs,
device).getOverallValidationResult(), PASS);
verify(supplyChainValidationSummaryDBManager).save(any(SupplyChainValidationSummary.class));
// verify the certs were updated with the test device object and saved in the cert man
ArgumentCaptor<DeviceAssociatedCertificate> certificatesCaptor
= ArgumentCaptor.forClass(DeviceAssociatedCertificate.class);
verify(certificateManager, times(3)).update(certificatesCaptor.capture());
List<DeviceAssociatedCertificate> certificateArgs = certificatesCaptor.getAllValues();
for (DeviceAssociatedCertificate certArg : certificateArgs) {
verify(certArg, atLeast(1)).setDevice(device);
}
}
/**
* All validations enabled, fail EC.
*/
@Test
public final void testFailEcValidation() {
when(policy.isEcValidationEnabled()).thenReturn(true);
when(policy.isPcValidationEnabled()).thenReturn(true);
when(policy.isPcAttributeValidationEnabled()).thenReturn(true);
when(policy.isExpiredCertificateValidationEnabled()).thenReturn(true);
doReturn(new AppraisalStatus(FAIL, "")).when(supplyChainCredentialValidator).
validateEndorsementCredential(eq(ec), any(KeyStore.class), any(Boolean.class));
doReturn(new AppraisalStatus(PASS, "")).when(supplyChainCredentialValidator)
.validatePlatformCredential(eq(pc), any(KeyStore.class), eq(true));
doReturn(new AppraisalStatus(PASS, "")).when(supplyChainCredentialValidator)
.validatePlatformCredential(eq(delta), any(KeyStore.class), eq(true));
doReturn(new AppraisalStatus(PASS, "")).when(supplyChainCredentialValidator)
.validatePlatformCredentialAttributes(eq(pc), any(DeviceInfoReport.class),
any(EndorsementCredential.class));
doReturn(new AppraisalStatus(PASS, "")).when(supplyChainCredentialValidator)
.validateDeltaPlatformCredentialAttributes(eq(delta), any(DeviceInfoReport.class),
eq(pc), anyMapOf(PlatformCredential.class, SupplyChainValidation.class));
Assert.assertEquals(service.validateSupplyChain(ec, pcs,
device).getOverallValidationResult(), FAIL);
verify(supplyChainValidationSummaryDBManager).save(any(SupplyChainValidationSummary.class));
}
/**
* All validations enabled, fail Pc Cert.
*/
@Test
public final void testFailPcValidation() {
when(policy.isEcValidationEnabled()).thenReturn(true);
when(policy.isPcValidationEnabled()).thenReturn(true);
when(policy.isPcAttributeValidationEnabled()).thenReturn(true);
when(policy.isExpiredCertificateValidationEnabled()).thenReturn(true);
doReturn(new AppraisalStatus(PASS, "")).when(supplyChainCredentialValidator).
validateEndorsementCredential(eq(ec), any(KeyStore.class), any(Boolean.class));
doReturn(new AppraisalStatus(FAIL, "")).when(supplyChainCredentialValidator)
.validatePlatformCredential(eq(pc), any(KeyStore.class), eq(true));
doReturn(new AppraisalStatus(FAIL, "")).when(supplyChainCredentialValidator)
.validatePlatformCredential(eq(delta), any(KeyStore.class), eq(true));
doReturn(new AppraisalStatus(PASS, "")).when(supplyChainCredentialValidator)
.validatePlatformCredentialAttributes(eq(pc), any(DeviceInfoReport.class),
any(EndorsementCredential.class));
doReturn(new AppraisalStatus(PASS, "")).when(supplyChainCredentialValidator)
.validateDeltaPlatformCredentialAttributes(eq(delta), any(DeviceInfoReport.class),
eq(pc), anyMapOf(PlatformCredential.class, SupplyChainValidation.class));
Assert.assertEquals(service.validateSupplyChain(ec, pcs,
device).getOverallValidationResult(), FAIL);
verify(supplyChainValidationSummaryDBManager).save(any(SupplyChainValidationSummary.class));
}
/**
* All validations enabled, Pc Attrib. fails.
*/
@Test
public final void testFailPcAttributeValidation() {
when(policy.isEcValidationEnabled()).thenReturn(true);
when(policy.isPcValidationEnabled()).thenReturn(true);
when(policy.isPcAttributeValidationEnabled()).thenReturn(true);
when(policy.isExpiredCertificateValidationEnabled()).thenReturn(true);
doReturn(new AppraisalStatus(PASS, "")).when(supplyChainCredentialValidator).
validateEndorsementCredential(eq(ec), any(KeyStore.class), eq(true));
doReturn(new AppraisalStatus(PASS, "")).when(supplyChainCredentialValidator)
.validatePlatformCredential(eq(pc), any(KeyStore.class), eq(true));
doReturn(new AppraisalStatus(PASS, "")).when(supplyChainCredentialValidator)
.validatePlatformCredential(eq(delta), any(KeyStore.class), eq(true));
doReturn(new AppraisalStatus(FAIL, "")).when(supplyChainCredentialValidator)
.validatePlatformCredentialAttributes(eq(pc), any(DeviceInfoReport.class),
any(EndorsementCredential.class));
doReturn(new AppraisalStatus(FAIL, "")).when(supplyChainCredentialValidator)
.validateDeltaPlatformCredentialAttributes(eq(delta), any(DeviceInfoReport.class),
eq(pc), anyMapOf(PlatformCredential.class, SupplyChainValidation.class));
Assert.assertEquals(service.validateSupplyChain(ec, pcs,
device).getOverallValidationResult(), FAIL);
verify(supplyChainValidationSummaryDBManager).save(any(SupplyChainValidationSummary.class));
}
/**
* Ec not enabled, all others pass.
*/
@Test
public final void testNoEcValidation() {
when(policy.isEcValidationEnabled()).thenReturn(false);
when(policy.isPcValidationEnabled()).thenReturn(true);
when(policy.isPcAttributeValidationEnabled()).thenReturn(true);
when(policy.isExpiredCertificateValidationEnabled()).thenReturn(true);
doReturn(new AppraisalStatus(FAIL, "")).when(supplyChainCredentialValidator).
validateEndorsementCredential(eq(ec), any(KeyStore.class), eq(true));
doReturn(new AppraisalStatus(PASS, "")).when(supplyChainCredentialValidator)
.validatePlatformCredential(eq(pc), any(KeyStore.class), eq(true));
doReturn(new AppraisalStatus(PASS, "")).when(supplyChainCredentialValidator)
.validatePlatformCredential(eq(delta), any(KeyStore.class), eq(true));
doReturn(new AppraisalStatus(PASS, "")).when(supplyChainCredentialValidator)
.validatePlatformCredentialAttributes(eq(pc), any(DeviceInfoReport.class),
any(EndorsementCredential.class));
doReturn(new AppraisalStatus(PASS, "")).when(supplyChainCredentialValidator)
.validateDeltaPlatformCredentialAttributes(eq(delta), any(DeviceInfoReport.class),
eq(pc), anyMapOf(PlatformCredential.class, SupplyChainValidation.class));
Assert.assertEquals(service.validateSupplyChain(ec, pcs,
device).getOverallValidationResult(), PASS);
verify(supplyChainValidationSummaryDBManager).save(any(SupplyChainValidationSummary.class));
}
/**
* Pc cert not enabled, all others pass.
*/
@Test
public final void testNoPcValidation() {
when(policy.isEcValidationEnabled()).thenReturn(true);
when(policy.isPcValidationEnabled()).thenReturn(false);
when(policy.isPcAttributeValidationEnabled()).thenReturn(true);
when(policy.isExpiredCertificateValidationEnabled()).thenReturn(true);
doReturn(new AppraisalStatus(PASS, "")).when(supplyChainCredentialValidator).
validateEndorsementCredential(eq(ec), any(KeyStore.class), eq(true));
doReturn(new AppraisalStatus(FAIL, "")).when(supplyChainCredentialValidator)
.validatePlatformCredential(eq(pc), any(KeyStore.class), eq(true));
doReturn(new AppraisalStatus(FAIL, "")).when(supplyChainCredentialValidator)
.validatePlatformCredential(eq(delta), any(KeyStore.class), eq(true));
doReturn(new AppraisalStatus(PASS, "")).when(supplyChainCredentialValidator)
.validatePlatformCredentialAttributes(eq(pc), any(DeviceInfoReport.class),
any(EndorsementCredential.class));
doReturn(new AppraisalStatus(PASS, "")).when(supplyChainCredentialValidator)
.validateDeltaPlatformCredentialAttributes(eq(delta), any(DeviceInfoReport.class),
eq(pc), anyMapOf(PlatformCredential.class, SupplyChainValidation.class));
Assert.assertEquals(service.validateSupplyChain(ec, pcs,
device).getOverallValidationResult(), FAIL);
verify(supplyChainValidationSummaryDBManager).save(any(SupplyChainValidationSummary.class));
}
/**
* Pc attrib not enabled, all others pass.
*/
@Test
public final void testNoPcAttributeValidation() {
when(policy.isEcValidationEnabled()).thenReturn(true);
when(policy.isPcValidationEnabled()).thenReturn(true);
when(policy.isPcAttributeValidationEnabled()).thenReturn(false);
when(policy.isExpiredCertificateValidationEnabled()).thenReturn(true);
doReturn(new AppraisalStatus(PASS, "")).when(supplyChainCredentialValidator).
validateEndorsementCredential(eq(ec), any(KeyStore.class), eq(true));
doReturn(new AppraisalStatus(PASS, "")).when(supplyChainCredentialValidator)
.validatePlatformCredential(eq(pc), any(KeyStore.class), eq(true));
doReturn(new AppraisalStatus(PASS, "")).when(supplyChainCredentialValidator)
.validatePlatformCredential(eq(delta), any(KeyStore.class), eq(true));
doReturn(new AppraisalStatus(FAIL, "")).when(supplyChainCredentialValidator)
.validatePlatformCredentialAttributes(eq(pc), any(DeviceInfoReport.class),
any(EndorsementCredential.class));
doReturn(new AppraisalStatus(FAIL, "")).when(supplyChainCredentialValidator)
.validateDeltaPlatformCredentialAttributes(eq(delta), any(DeviceInfoReport.class),
eq(pc), anyMapOf(PlatformCredential.class, SupplyChainValidation.class));
Assert.assertEquals(service.validateSupplyChain(ec, pcs,
device).getOverallValidationResult(), PASS);
verify(supplyChainValidationSummaryDBManager).save(any(SupplyChainValidationSummary.class));
}
/**
* All enabled, EC is null.
*/
@Test
public final void testNullEcValidation() {
when(policy.isEcValidationEnabled()).thenReturn(true);
Assert.assertEquals(service.validateSupplyChain(null, pcs,
device).getOverallValidationResult(), FAIL);
}
/**
* All enabled, PC is null. Then PC set is empty.
*/
@Test
public final void testNullPcValidation() {
when(policy.isEcValidationEnabled()).thenReturn(false);
when(policy.isPcValidationEnabled()).thenReturn(true);
Assert.assertEquals(service.validateSupplyChain(ec, null,
device).getOverallValidationResult(), FAIL);
final HashSet<PlatformCredential> emptySet = new HashSet<>();
Assert.assertEquals(service.validateSupplyChain(ec, emptySet,
device).getOverallValidationResult(), FAIL);
}
/**
* All enabled, PC is null. Then PC set is empty.
*/
@Test
public final void testNullPcAttributeValidation() {
when(policy.isEcValidationEnabled()).thenReturn(false);
when(policy.isPcValidationEnabled()).thenReturn(false);
when(policy.isPcAttributeValidationEnabled()).thenReturn(true);
Assert.assertEquals(service.validateSupplyChain(ec, null,
device).getOverallValidationResult(), FAIL);
final HashSet<PlatformCredential> emptySet = new HashSet<>();
Assert.assertEquals(service.validateSupplyChain(ec, emptySet,
device).getOverallValidationResult(), FAIL);
}
/**
* Puts an EC, STM CA, and GS CA in the DB, attempts to retrieve the CAs from the EC.
* @throws URISyntaxException failed to parse certificate file location.
* @throws IOException couldn't create certificates from file.
* @throws KeyStoreException was unable to retrieve keystore.
*/
@Test
public final void testGetCaChain() throws URISyntaxException, IOException, KeyStoreException {
CertificateManager realCertMan = new DBCertificateManager(sessionFactory);
// the main service in this class only uses mocked managers, we need a real DB certificate
// manager for this test, so we make a second service.
SupplyChainValidationServiceImpl mostlyMockedService = new SupplyChainValidationServiceImpl(
policyManager,
appraiserManager,
realCertMan,
null,
supplyChainValidationSummaryDBManager,
supplyChainCredentialValidator,
referenceDigestManager,
referenceEventManager
);
CertificateAuthorityCredential globalSignCaCert = new CertificateAuthorityCredential(
Files.readAllBytes(Paths.get(getClass().getResource(
GS_ROOT_CA).toURI())));
CertificateAuthorityCredential rootCa = new CertificateAuthorityCredential(
Files.readAllBytes(Paths.get(getClass().getResource(
STM_ROOT_CA).toURI()))
);
EndorsementCredential endorsementCredential = new EndorsementCredential(
Files.readAllBytes(Paths.get(getClass().getResource(
NUC1_EC).toURI())));
realCertMan.save(endorsementCredential);
realCertMan.save(rootCa);
realCertMan.save(globalSignCaCert);
KeyStore ks = mostlyMockedService.getCaChain(endorsementCredential);
String stmCaAlias = rootCa.getId().toString();
String gsCaAlias = globalSignCaCert.getId().toString();
// cyrus-dev note: these were changed to fail so the unit test
// passes. #308 changes how the CAs are looked up and these
// tests certificates don't match up with SKI or AKI
// and the issuer O= matches but the #308 changes make it
// so that the entire string matches because O= is not
// a required field.
Assert.assertEquals(ks.size(), 0);
Assert.assertNull(ks.getCertificate(stmCaAlias));
Assert.assertNull(ks.getCertificate(gsCaAlias));
realCertMan.delete(endorsementCredential);
realCertMan.delete(rootCa);
realCertMan.delete(globalSignCaCert);
}
/**
* Puts an EC, and STM CA in the DB, attempts to retrieve the CAs from the EC. The STM CA
* points to a GS CA that is not present.
* @throws URISyntaxException failed to parse certificate file location.
* @throws IOException couldn't create certificates from file.
* @throws KeyStoreException was unable to retrieve keystore.
*/
@Test
public final void testGetNotFullCaChain() throws URISyntaxException, IOException,
KeyStoreException {
CertificateManager realCertMan = new DBCertificateManager(sessionFactory);
// the main service in this class only uses mocked managers, we need a real DB certificate
// manager for this test, so we make a second service.
SupplyChainValidationServiceImpl mostlyMockedService = new SupplyChainValidationServiceImpl(
policyManager,
appraiserManager,
realCertMan,
null,
supplyChainValidationSummaryDBManager,
supplyChainCredentialValidator,
referenceDigestManager,
referenceEventManager
);
CertificateAuthorityCredential rootCa = new CertificateAuthorityCredential(
Files.readAllBytes(Paths.get(getClass().getResource(
STM_ROOT_CA).toURI()))
);
EndorsementCredential endorsementCredential = new EndorsementCredential(
Files.readAllBytes(Paths.get(getClass().getResource(
NUC1_EC).toURI())));
realCertMan.save(endorsementCredential);
realCertMan.save(rootCa);
KeyStore ks = mostlyMockedService.getCaChain(endorsementCredential);
String stmCaAlias = rootCa.getId().toString();
// see cyrus-dev note above
Assert.assertNull(ks.getCertificate(stmCaAlias));
Assert.assertEquals(ks.size(), 0);
realCertMan.delete(endorsementCredential);
realCertMan.delete(rootCa);
}
/**
* Puts an EC in the DB, attempts to retrieve the CA from the EC.
* @throws URISyntaxException failed to parse certificate file location.
* @throws IOException couldn't create certificates from file.
* @throws KeyStoreException was unable to retrieve keystore.
*/
@Test
public final void testGetEmptyCaChain() throws URISyntaxException, IOException,
KeyStoreException {
CertificateManager realCertMan = new DBCertificateManager(sessionFactory);
// the main service in this class only uses mocked managers, we need a real DB certificate
// manager for this test, so we make a second service.
SupplyChainValidationServiceImpl mostlyMockedService = new SupplyChainValidationServiceImpl(
policyManager,
appraiserManager,
realCertMan,
null,
supplyChainValidationSummaryDBManager,
supplyChainCredentialValidator,
referenceDigestManager,
referenceEventManager
);
EndorsementCredential endorsementCredential = new EndorsementCredential(
Files.readAllBytes(Paths.get(getClass().getResource(
NUC1_EC).toURI())));
realCertMan.save(endorsementCredential);
KeyStore ks = mostlyMockedService.getCaChain(endorsementCredential);
Assert.assertEquals(ks.size(), 0);
realCertMan.delete(endorsementCredential);
}
/**
* Puts an EC, STM CA, GS CA, and an Intel CA in the DB, attempts to retrieve the CAs
* from the EC.
* @throws URISyntaxException failed to parse certificate file location.
* @throws IOException couldn't create certificates from file.
* @throws KeyStoreException was unable to retrieve keystore.
*/
@Test
public final void testGetCaChainWithExtraCerts() throws URISyntaxException, IOException,
KeyStoreException {
CertificateManager realCertMan = new DBCertificateManager(sessionFactory);
// the main service in this class only uses mocked managers, we need a real DB certificate
// manager for this test, so we make a second service.
SupplyChainValidationServiceImpl mostlyMockedService = new SupplyChainValidationServiceImpl(
policyManager,
appraiserManager,
realCertMan,
null,
supplyChainValidationSummaryDBManager,
supplyChainCredentialValidator,
referenceDigestManager,
referenceEventManager
);
CertificateAuthorityCredential globalSignCaCert = new CertificateAuthorityCredential(
Files.readAllBytes(Paths.get(getClass().getResource(
GS_ROOT_CA).toURI())));
CertificateAuthorityCredential rootCa = new CertificateAuthorityCredential(
Files.readAllBytes(Paths.get(getClass().getResource(
STM_ROOT_CA).toURI()))
);
CertificateAuthorityCredential intelCa = new CertificateAuthorityCredential(
Files.readAllBytes(Paths.get(getClass().getResource(
INTEL_CA).toURI()))
);
EndorsementCredential endorsementCredential = new EndorsementCredential(
Files.readAllBytes(Paths.get(getClass().getResource(
NUC1_EC).toURI())));
realCertMan.save(endorsementCredential);
realCertMan.save(rootCa);
realCertMan.save(globalSignCaCert);
realCertMan.save(intelCa);
KeyStore ks = mostlyMockedService.getCaChain(endorsementCredential);
String stmCaAlias = rootCa.getId().toString();
String gsCaAlias = globalSignCaCert.getId().toString();
// See cyrus-dev note above
Assert.assertNull(ks.getCertificate(stmCaAlias));
Assert.assertNull(ks.getCertificate(gsCaAlias));
Assert.assertEquals(ks.size(), 0);
realCertMan.delete(endorsementCredential);
realCertMan.delete(rootCa);
realCertMan.delete(globalSignCaCert);
realCertMan.delete(intelCa);
}
/**
* Puts an Intel PC and Intel CA in the DB, attempts to retrieve the CA from the PC.
* @throws URISyntaxException failed to parse certificate file location.
* @throws IOException couldn't create certificates from file.
* @throws KeyStoreException was unable to retrieve keystore.
*/
@Test
public final void testGetPcCaChain() throws URISyntaxException, IOException, KeyStoreException {
CertificateManager realCertMan = new DBCertificateManager(sessionFactory);
// the main service in this class only uses mocked managers, we need a real DB certificate
// manager for this test, so we make a second service.
SupplyChainValidationServiceImpl mostlyMockedService = new SupplyChainValidationServiceImpl(
policyManager,
appraiserManager,
realCertMan,
null,
supplyChainValidationSummaryDBManager,
supplyChainCredentialValidator,
referenceDigestManager,
referenceEventManager
);
CertificateAuthorityCredential intelCa = new CertificateAuthorityCredential(
Files.readAllBytes(Paths.get(getClass().getResource(
INTEL_CA).toURI()))
);
PlatformCredential platformCredential = new PlatformCredential(
Files.readAllBytes(Paths.get(getClass().getResource(
NUC_PC).toURI())));
realCertMan.save(platformCredential);
realCertMan.save(intelCa);
KeyStore ks = mostlyMockedService.getCaChain(platformCredential);
String intelCaAlias = intelCa.getId().toString();
Assert.assertNotNull(ks.getCertificate(intelCaAlias));
Assert.assertEquals(ks.size(), 1);
realCertMan.delete(platformCredential);
realCertMan.delete(intelCa);
}
/**
* Puts an Intel PC, STM CA, and GS CA in the DB, attempts to retrieve the CAs from the PC. None
* should match.
* @throws URISyntaxException failed to parse certificate file location.
* @throws IOException couldn't create certificates from file.
* @throws KeyStoreException was unable to retrieve keystore.
*/
@Test
public final void testGetPcCaChainNoMatches() throws URISyntaxException, IOException,
KeyStoreException {
CertificateManager realCertMan = new DBCertificateManager(sessionFactory);
// the main service in this class only uses mocked managers, we need a real DB certificate
// manager for this test, so we make a second service.
SupplyChainValidationServiceImpl mostlyMockedService = new SupplyChainValidationServiceImpl(
policyManager,
appraiserManager,
realCertMan,
null,
supplyChainValidationSummaryDBManager,
supplyChainCredentialValidator,
referenceDigestManager,
referenceEventManager
);
CertificateAuthorityCredential globalSignCaCert = new CertificateAuthorityCredential(
Files.readAllBytes(Paths.get(getClass().getResource(
GS_ROOT_CA).toURI())));
CertificateAuthorityCredential rootCa = new CertificateAuthorityCredential(
Files.readAllBytes(Paths.get(getClass().getResource(
STM_ROOT_CA).toURI()))
);
PlatformCredential platformCredential = new PlatformCredential(
Files.readAllBytes(Paths.get(getClass().getResource(
NUC_PC).toURI())));
realCertMan.save(platformCredential);
realCertMan.save(rootCa);
realCertMan.save(globalSignCaCert);
KeyStore ks = mostlyMockedService.getCaChain(platformCredential);
Assert.assertEquals(ks.size(), 0);
realCertMan.delete(platformCredential);
realCertMan.delete(rootCa);
realCertMan.delete(globalSignCaCert);
}
/**
* Puts an STM intermediate CA, STM 'root' CA, and GlobalSign root CA into the in-memory
* database, and then runs supply chain validation on a given endorsement credential.
*
* @throws URISyntaxException if building the path to a certificate resource fails
* @throws IOException if there is a problem deserializing a certificate
*/
@Test
public void testVerifyEcAgainstCaChain() throws URISyntaxException, IOException {
when(policy.isEcValidationEnabled()).thenReturn(true);
when(policy.isPcValidationEnabled()).thenReturn(false);
when(policy.isPcAttributeValidationEnabled()).thenReturn(false);
CertificateManager realCertMan = new DBCertificateManager(sessionFactory);
Device storedDevice = getStoredTestDevice();
SupplyChainValidationServiceImpl mostlyMockedService = new SupplyChainValidationServiceImpl(
policyManager,
appraiserManager,
realCertMan,
null,
supplyChainValidationSummaryDBManager,
new SupplyChainCredentialValidator(),
referenceDigestManager,
referenceEventManager
);
CertificateAuthorityCredential stmEkRootCa = new CertificateAuthorityCredential(
Files.readAllBytes(Paths.get(getClass().getResource(
STM_ROOT_CA).toURI())));
CertificateAuthorityCredential stmTpmEkIntermediateCA = new CertificateAuthorityCredential(
Files.readAllBytes(Paths.get(getClass().getResource(
STM_TPM_EK_INTERMEDIATE_CA_02).toURI())));
CertificateAuthorityCredential globalSignTpmRoot = new CertificateAuthorityCredential(
Files.readAllBytes(Paths.get(getClass().getResource(
GS_ROOT_CA).toURI()))
);
realCertMan.save(stmTpmEkIntermediateCA);
realCertMan.save(globalSignTpmRoot);
realCertMan.save(stmEkRootCa);
EndorsementCredential nucEc = new EndorsementCredential(
Files.readAllBytes(Paths.get(getClass().getResource(NUC_EC).toURI()))
);
realCertMan.save(nucEc);
SupplyChainValidationSummary summary = mostlyMockedService.validateSupplyChain(
nucEc, Collections.emptySet(), storedDevice
);
Assert.assertEquals(summary.getOverallValidationResult(), PASS);
for (SupplyChainValidation validation : summary.getValidations()) {
Assert.assertEquals(
validation.getValidationType(),
SupplyChainValidation.ValidationType.ENDORSEMENT_CREDENTIAL
);
}
// verify the EC was updated with the test device object and saved in the cert man
EndorsementCredential updatedStoredEc =
EndorsementCredential.select(realCertMan).bySerialNumber(nucEc.getSerialNumber())
.getCertificate();
Assert.assertEquals(updatedStoredEc.getDevice().getId(), storedDevice.getId());
realCertMan.delete(stmTpmEkIntermediateCA);
realCertMan.delete(globalSignTpmRoot);
realCertMan.delete(stmEkRootCa);
realCertMan.delete(nucEc);
}
private Device getStoredTestDevice() {
DeviceManager deviceManager = new DBDeviceManager(sessionFactory);
DeviceGroupManager deviceGroupManager = new DBDeviceGroupManager(sessionFactory);
DeviceGroup testGroup = new DeviceGroup("group1");
Device testDevice = new Device("SCVSI-test");
testDevice.setDeviceGroup(deviceGroupManager.saveDeviceGroup(testGroup));
return deviceManager.saveDevice(testDevice);
}
}

View File

@ -1,19 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIDrzCCApegAwIBAgIEJVDCpzANBgkqhkiG9w0BAQUFADCBhzELMAkGA1UEBhMCVVMxCzAJBgNV
BAgTAkNBMRQwEgYDVQQHEwtTYW50YSBDbGFyYTEaMBgGA1UEChMRSW50ZWwgQ29ycG9yYXRpb24x
ITAfBgNVBAsTGFRyYW5zcGFyZW50IFN1cHBseSBDaGFpbjEWMBQGA1UEAxMNd3d3LmludGVsLmNv
bTAeFw0xNzA0MTkwMDAyMTBaFw0zNzEwMzEwMDAyMTBaMIGHMQswCQYDVQQGEwJVUzELMAkGA1UE
CBMCQ0ExFDASBgNVBAcTC1NhbnRhIENsYXJhMRowGAYDVQQKExFJbnRlbCBDb3Jwb3JhdGlvbjEh
MB8GA1UECxMYVHJhbnNwYXJlbnQgU3VwcGx5IENoYWluMRYwFAYDVQQDEw13d3cuaW50ZWwuY29t
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAo7Cu8Y3uWKoTQW/RnmNJG5h3PlYdvE2B
v0c2+WUz0AprrVMpUvbJaNoMx47ev9CYvoxuJU0t9JjMm2i1u6Ol6VlK5yAWqr/qySvkryKTUuSx
7sOtkayF4LqD16LlTIAmUhC0KabbrEitpBa1BtKsy8yhGjbm+bYs0D19jjqHH9rOT3I8j04tc5a4
r86dPuCZX8RJTV8tZd51ULFhl+70rIwPY/Ecbl5lzx786v9xW3XnC4/XplvMJXw2wkUo8G98qyeD
Odvha4Y49NswlqyUHnAlk+n4bIF7cvSDi+vb4YRaV2g5u2K3290ePUcNyzGTYgdpBnvOQlna/IZ0
NifXQQIDAQABoyEwHzAdBgNVHQ4EFgQUIQWBMO10djQycITwJ5XfZw6L13MwDQYJKoZIhvcNAQEF
BQADggEBADgcp+GI5S6HSVhQD7wyqKIBCLUk5nC0FzE0W1vBuLrwCagXHXwMC09uDXOXtfNOoQVZ
duS3oaju5i40lSbFmp7V2hewQo6vlum/M05Lg/O7vndKxnKss/PeT5jMyjJ4t00HQ3KEm2aZL1BR
AJZjbH+hZ08bh5pY5uP7husjjcur0xqhFw9Afq4SUbXtA47QBaUuJTtBwTf2r/Nesq4a6zRAtqF/
gmGD8VOSahBrtuIS30GRgcpsk6kKO4fLCvCZwyJ2/Mn+ySMB3+G6XqgDekkykCnnvMVUr+nzasug
d/4I62L3eZ2j5Xpa3O1kFZ+zIMrbLRh7Bu8OdjFdvodwmRU=
-----END CERTIFICATE-----

View File

@ -1,26 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIEXjCCA0agAwIBAgIUS5gujeW5kYvYdMJZlIUT6s3F0cwwDQYJKoZIhvcNAQEF
BQAwVTELMAkGA1UEBhMCQ0gxHjAcBgNVBAoTFVNUTWljcm9lbGVjdHJvbmljcyBO
VjEmMCQGA1UEAxMdU1RNIFRQTSBFSyBJbnRlcm1lZGlhdGUgQ0EgMDIwHhcNMTQw
MjIyMDAwMDAwWhcNMjQwMjIyMDAwMDAwWjAAMIIBNzAiBgkqhkiG9w0BAQcwFaIT
MBEGCSqGSIb3DQEBCQQEVENQQQOCAQ8AMIIBCgKCAQEAsdTxu5pRjEOgA0tCNYgn
NmAqLzIxBTBft4pMBGdEk922dvBLvQySN13YnvVF6FnYCc0Y+5hSAZiRCcXpr/M3
6wx5YkePCPss06KQMujy3X9jwxTU0cDbKTjKCmFpQqCqiGIk2f7mss8yIABlwT3R
cBBbcDpGn2wYi5s9UhUfCOQ6D7qEPKJEi5IQC7/oyu5zT5FMUANdsebxrYpALcKK
8/mp5Rwj+xmaAg/+OC9jIeFGLYYu/hQr/1BPYSVicfuIFdc/0VzyJO5KMRozvV3I
2dbzQwqUD4xUxPR+f7VC+3p641Mb7WobIZH7wJm2k0M8HWeErytA66WtAoueU89O
iQIDAQABo4IBZDCCAWAwHwYDVR0jBBgwFoAUVx+Aa0fM55v6NZR87Yi40QBa4J4w
QgYDVR0gBDswOTA3BgRVHSAAMC8wLQYIKwYBBQUHAgEWIWh0dHA6Ly93d3cuc3Qu
Y29tL1RQTS9yZXBvc2l0b3J5LzBVBgNVHREBAf8ESzBJpEcwRTEWMBQGBWeBBQIB
DAtpZDo1MzU0NEQyMDEXMBUGBWeBBQICDAxTVDMzWlAyNFBWU1AxEjAQBgVngQUC
AwwHaWQ6MEQwQzB/BgNVHQkEeDB2MBYGBWeBBQIQMQ0wCwwDMS4yAgECAgF0MCAG
BWeBBQISMRcwFQIBAAEB/6ADCgEBoQMKAQCiAwoBADA6BgNVBTQxMzAkMCIGCSqG
SIb3DQEBBzAVohMwEQYJKoZIhvcNAQEJBARUQ1BBMAswCQYFKw4DAhoFADAMBgNV
HRMBAf8EAjAAMBMGA1UdJQEB/wQJMAcGBWeBBQgBMA0GCSqGSIb3DQEBBQUAA4IB
AQAb50G/d9D18ahy6RScXObaazgrNZHcF0otH9W1uJzXgSQPjFFYbHAh2+EGI8uD
90Hj9XgZYmcGv0pUHcFw7msNamr3c/Or8+pLPnu5OZtr4jCEZ7/Z75v0Z825Ov8R
N+JIxB9RT0Yd3KAPQsp4d45NHWOPBQPgBi/pW/eJqPO2MJD0uraRqAlNrUD3ppc7
xxsmOoOhyUFcs14KyrgIWNazx+4EElAKU3PthU70cszFAQM2hw/EYBfRwQ5rVZd7
V2x9hMC4POgACE6gVIDV/mHoZe6AfGQKveblJEX9gOccI28vnT14d0CwhN/SvgZF
JigA9V7w26ecFRWXpm79utMU
-----END CERTIFICATE-----

View File

@ -1,26 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIEXjCCA0agAwIBAgIUBwCBhWf/NXkWkNLUBJRd9WsObccwDQYJKoZIhvcNAQEF
BQAwVTELMAkGA1UEBhMCQ0gxHjAcBgNVBAoTFVNUTWljcm9lbGVjdHJvbmljcyBO
VjEmMCQGA1UEAxMdU1RNIFRQTSBFSyBJbnRlcm1lZGlhdGUgQ0EgMDIwHhcNMTQw
MjIzMDAwMDAwWhcNMjQwMjIzMDAwMDAwWjAAMIIBNzAiBgkqhkiG9w0BAQcwFaIT
MBEGCSqGSIb3DQEBCQQEVENQQQOCAQ8AMIIBCgKCAQEAvcrvqYUTomoUC5zk5Jhd
myVzoEe94eXX1YFHyElCCpLM4/86ZbADKTHeGwygR4AWClb0Jmmloj+aIRUY3pZD
2GVxDnmD9CBS+60doM1cN0+D01hhg7J/dnaigAbFxPZauSyV9XfTqq1MQlxWpUEf
J4IALc+MhVd0kqSzzqSDxoneu83w1Ssvmah73wqWpansqQRYr1D7ABbkvouO56iu
4z6UditUSbrk3FrZBs+e73tzy9OAzQBg617kU+BKhHCRuRIPYk3tPXHq53Y7Jwvf
CkiEVWAU+MEMZJc/RRIOnWdSdDMxHZVnaxywrC8KUKZ1G3id/GVJfeivPxZVRBdh
sQIDAQABo4IBZDCCAWAwHwYDVR0jBBgwFoAUVx+Aa0fM55v6NZR87Yi40QBa4J4w
QgYDVR0gBDswOTA3BgRVHSAAMC8wLQYIKwYBBQUHAgEWIWh0dHA6Ly93d3cuc3Qu
Y29tL1RQTS9yZXBvc2l0b3J5LzBVBgNVHREBAf8ESzBJpEcwRTEWMBQGBWeBBQIB
DAtpZDo1MzU0NEQyMDEXMBUGBWeBBQICDAxTVDMzWlAyNFBWU1AxEjAQBgVngQUC
AwwHaWQ6MEQwQzB/BgNVHQkEeDB2MBYGBWeBBQIQMQ0wCwwDMS4yAgECAgF0MCAG
BWeBBQISMRcwFQIBAAEB/6ADCgEBoQMKAQCiAwoBADA6BgNVBTQxMzAkMCIGCSqG
SIb3DQEBBzAVohMwEQYJKoZIhvcNAQEJBARUQ1BBMAswCQYFKw4DAhoFADAMBgNV
HRMBAf8EAjAAMBMGA1UdJQEB/wQJMAcGBWeBBQgBMA0GCSqGSIb3DQEBBQUAA4IB
AQAAbZng7i2L22p05GpbURYk6o7bYH3LZ+nusEGvi0tRkpqr9Qc8vMp8fgYQMaZV
8QiDa5JfYD3vzOjQBRvUdqz8UrzemsuErk4w3yzsBh2lIY54jcXWmJFVk4HVp2wV
xL5EysIII9Fkt2gfcoPSGyIDX4p83Vou5nhNOQPowahMuS6BUfcBKzMM7pK40GUj
N+cijK61zPkvAQArEkAnVNuTxvLS41WW3x1kTtkLUPuTh7SynNAYwoVfl19uNPOs
UTxDrFA7But7Vo0xoj+zSBQqzk0Gp3Pldw6mOUc3uI1UBmVtQGRy7cgsbLJ3bu/6
fLuuAG5/ywVpo4MiG+PkFYXh
-----END CERTIFICATE-----

View File

@ -1,94 +1,77 @@
apply plugin: 'checkstyle'
apply plugin: 'findbugs'
apply plugin: 'java'
apply plugin: 'jacoco'
apply plugin: 'pmd'
apply plugin: 'war'
plugins {
id 'application'
id 'java'
id 'war'
id "nebula.ospackage" version "9.1.1"
id 'org.springframework.boot' version '3.0.1'
id 'io.spring.dependency-management' version '1.1.0'
}
sourceCompatibility = 1.8
java {
toolchain {
languageVersion = JavaLanguageVersion.of(17)
}
}
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
repositories {
flatDir {
dirs 'libs'
}
flatDir { dirs "lib" }
mavenCentral()
}
dependencies {
providedCompile 'javax.servlet:javax.servlet-api:3.1.0' //libs.servlet_api
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.plugin:spring-plugin-core:3.0.0'
implementation 'org.apache.httpcomponents:httpclient:4.5.7'
implementation 'com.google.guava:guava:31.1-jre'
implementation 'org.glassfish.web:jakarta.servlet.jsp.jstl:3.0.0'
implementation 'org.apache.httpcomponents.client5:httpclient5:5.2.1'
implementation 'commons-codec:commons-codec:1.15'
implementation 'org.apache.logging.log4j:log4j-core:2.19.0'
implementation 'org.apache.logging.log4j:log4j-api:2.19.0'
compileOnly 'org.projectlombok:lombok'
runtimeOnly 'org.mariadb.jdbc:mariadb-java-client'
annotationProcessor 'org.projectlombok:lombok'
providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat'
compile(project(':HIRS_Utils')) {
exclude module: "javassist"
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
compile(project(':HIRS_AttestationCA'))
compile libs.commons_io
compile libs.commons_lang
compile libs.commons_upload
compile libs.gson
compile libs.guava // for com.google.common
compile libs.hibernate
compile libs.http
compile libs.jstl
compile libs.log4j2
compile libs.log4j2_web
compile libs.pci_ids
compile 'javax.servlet:javax.servlet-api:3.1.0' //libs.servlet_api
compile libs.spring_webmvc
compile 'org.springframework:spring-context-support:4.3.30.RELEASE'
compile 'org.hibernate:hibernate-validator:5.3.4.Final'
compileOnly libs.checkstyle
compileOnly libs.findbugs
runtime fileTree(dir: 'build/plugins', include: ['*.jar'])
testCompile 'org.hamcrest:hamcrest-all:1.3'
// override the servlet API for testing. Required for Spring Integration tests
testCompile 'javax.servlet:javax.servlet-api:3.1.0'
testCompile libs.hsqldb
testCompile libs.spring_test
testCompile libs.testng
testCompile libs.mockito
testCompile libs.testng
testCompile 'org.skyscreamer:jsonassert:1.2.3' // for .andExpect(content().json(json))
testRuntime 'com.jayway.jsonpath:json-path:2.1.0'
}
test {
useTestNG()
}
copyVersion.dependsOn compileJava
war.dependsOn copyVersion
war.dependsOn addPlugins
war {
from(buildDir) {
include 'VERSION'
into 'WEB-INF/classes'
}
archiveName = 'HIRS_AttestationCAPortal.war'
archiveFileName = 'HIRS_AttestationCAPortal.war'
}
ext.configDir = new File(projectDir, 'config')
ext.checkstyleConfigDir = "$configDir/checkstyle"
checkstyle {
toolVersion = '8.10.1'
configFile = checkstyleConfigFile
configProperties.put('basedir', checkstyleConfigDir)
ignoreFailures = false
showViolations = true
}
ext.findbugsConfigDir = "$configDir/findbugs"
findbugs {
toolVersion = '3.0.0'
ignoreFailures = false
effort = 'max'
excludeFilter = new File(findbugsConfigDir, 'suppressions.xml')
}
//ospackage {
// packageName = 'HIRS_AttestationCA'
// os = LINUX
// arch = NOARCH
// release = '1'
//
// user 'root'
// fileMode = 0755
//
// addParentDirs = true
// createDirectoryEntry true
//
// into ("/opt/tomcat/webapps") {
// from war.outputs.files
// from '../HIRS_AttestationCAPortal/build/libs/HIRS_AttestationCAPortal.war'
// user 'root'
// fileMode = 0755
// }
//
// buildRpm {
// arch = X86_64
// }
//}

View File

@ -0,0 +1,28 @@
package hirs.attestationca.portal;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.context.annotation.ComponentScan;
import java.util.Collections;
@SpringBootApplication
@EnableAutoConfiguration
@ComponentScan({"hirs.attestationca.portal", "hirs.attestationca.portal.page.controllers", "hirs.attestationca.portal.entity", "hirs.attestationca.portal.service"})
public class HIRSApplication extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(HIRSApplication.class);
}
public static void main(String[] args) {
SpringApplication springApplication = new SpringApplication(HIRSApplication.class);
springApplication.setDefaultProperties(Collections.singletonMap("server.servlet.context-path", "/portal"));
springApplication.run(args);
// SpringApplication.run(HIRSApplication.class, args);
}
}

View File

@ -0,0 +1,63 @@
package hirs.attestationca.portal;
import hirs.attestationca.portal.service.SettingsServiceImpl;
import jakarta.servlet.ServletContextListener;
import jakarta.servlet.annotation.WebListener;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
@WebListener
public class HIRSDbInitializer implements ServletContextListener {
private static final Logger LOGGER = LogManager.getLogger(HIRSDbInitializer.class);
@Autowired
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
@Autowired
static SettingsServiceImpl settingsService = new SettingsServiceImpl();
//
// public void contextInitialized(final ServletContextEvent servletContextEvent) {
//// AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
// context.getEnvironment().addActiveProfile("server");
// context.register(PersistenceJPAConfig.class);
// context.refresh();
//
// // obtain reference to hibernate session factory
// EntityManager entityManager = context.getBean(EntityManagerFactory.class)
// .createEntityManager();
// /**
// * This fails if there is an entry already.
// */
//// entityManager.getTransaction().begin();
//// entityManager.persist(context.getBean("default-settings"));
//// entityManager.getTransaction().commit();
//
// insertDefaultEntries();
// }
//
// /**
// * Insert the ACA's default entries into the DB. This class is invoked after successful
// * install of the HIRS_AttestationCA RPM.
// *
// */
// public static synchronized void insertDefaultEntries() {
// LOGGER.error("Ensuring default ACA database entries are present.");
//
// // If the SupplyChainAppraiser exists, do not attempt to re-save the supply chain appraiser
// // or SupplyChainSettings
//
// // Create the SupplyChainAppraiser
// LOGGER.error("Saving supply chain appraiser...");
//
//
// // Create the SupplyChainSettings
// LOGGER.error("Saving default supply chain policy...");
//// SupplyChainSettings supplyChainPolicy = new SupplyChainSettings(
//// SupplyChainSettings.DEFAULT_POLICY);
// settingsService.saveSettings(new SupplyChainSettings("Default", "Settings are configured for no validation flags set."));
//
// LOGGER.error("ACA database initialization complete.");
// }
}

View File

@ -0,0 +1,87 @@
package hirs.attestationca.portal;
import hirs.attestationca.portal.entity.userdefined.SupplyChainSettings;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
import org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.JpaVendorAdapter;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.sql.DataSource;
import java.util.Properties;
@Configuration
@EnableTransactionManagement
@PropertySource({ "classpath:hibernate.properties" })
@ComponentScan({ "hirs.attestationca.portal" })
@EnableJpaRepositories(basePackages = "hirs.attestationca.portal.entity")
public class PersistenceJPAConfig {
@Autowired
private Environment environment;
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
final LocalContainerEntityManagerFactoryBean entityManagerBean = new LocalContainerEntityManagerFactoryBean();
entityManagerBean.setDataSource(dataSource());
entityManagerBean.setPackagesToScan(new String[] {"hirs.attestationca.portal.entity"});
JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
entityManagerBean.setJpaVendorAdapter(vendorAdapter);
entityManagerBean.setJpaProperties(additionalProperties());
return entityManagerBean;
}
@Bean
public DataSource dataSource() {
final DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(environment.getProperty("hibernate.connection.driver_class"));
dataSource.setUrl(environment.getProperty("hibernate.connection.url"));
dataSource.setUsername(environment.getProperty("hibernate.connection.username"));
dataSource.setPassword(environment.getProperty("hibernate.connection.password"));
return dataSource;
}
@Bean
public PlatformTransactionManager transactionManager() {
final JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory().getObject());
return transactionManager;
}
@Bean
public PersistenceExceptionTranslationPostProcessor exceptionTranslation() {
return new PersistenceExceptionTranslationPostProcessor();
}
final Properties additionalProperties() {
final Properties hibernateProperties = new Properties();
hibernateProperties.setProperty("hibernate.hbm2ddl.auto",
environment.getProperty("hibernate.hbm2ddl.auto"));
hibernateProperties.setProperty("hibernate.dialect",
environment.getProperty("hibernate.dialect"));
hibernateProperties.setProperty("hibernate.cache.use_second_level_cache",
"false");
return hibernateProperties;
}
@Bean(name="default-settings")
public SupplyChainSettings supplyChainSettings() {
SupplyChainSettings scSettings = new SupplyChainSettings("Default", "Settings are configured for no validation flags set.");
return scSettings;
}
}

View File

@ -1,172 +0,0 @@
package hirs.attestationca.portal.datatables;
import javax.validation.constraints.NotNull;
import org.hibernate.validator.constraints.NotBlank;
/**
* Java representation of a jQuery DataTables Column.
*/
public class Column {
/**
* Default constructor.
*/
public Column() {
}
/**
* Constructor.
* @param data the data
* @param name the name
* @param searchable true if searchable
* @param orderable true if orderable
* @param search the Search structure.
*/
public Column(final String data, final String name, final boolean searchable,
final boolean orderable, final Search search) {
this.data = data;
this.name = name;
this.searchable = searchable;
this.orderable = orderable;
this.search = search;
}
/**
* Column's data source.
*
* @see http://datatables.net/reference/option/columns.data
*/
@NotBlank
private String data;
/**
* Column's name.
*
* @see http://datatables.net/reference/option/columns.name
*/
private String name;
/**
* Flag to indicate if this column is searchable (true) or not (false).
*
* @see http://datatables.net/reference/option/columns.searchable
*/
@NotNull
private boolean searchable;
/**
* Flag to indicate if this column is orderable (true) or not (false).
*
* @see http://datatables.net/reference/option/columns.orderable
*/
@NotNull
private boolean orderable;
/**
* Search value to apply to this specific column.
*/
@NotNull
private Search search;
/**
*
* @return the data
*/
public String getData() {
return data;
}
/**
* Sets the data.
* @param data the data
*/
public void setData(final String data) {
this.data = data;
}
/**
*
* @return the name
*/
public String getName() {
return name;
}
/**
* Sets the name.
* @param name the name
*/
public void setName(final String name) {
this.name = name;
}
/**
* Gets the searchable flag.
* @return true if searchable, false otherwise
*/
public boolean isSearchable() {
return searchable;
}
/**
* Sets the searchable flag.
* @param searchable true if searchable, false otherwise
*/
public void setSearchable(final boolean searchable) {
this.searchable = searchable;
}
/**
*
* @return true if orderable, false otherwise
*/
public boolean isOrderable() {
return orderable;
}
/**
* Sets the orderable flag.
* @param orderable true if orderable, false otherwise
*/
public void setOrderable(final boolean orderable) {
this.orderable = orderable;
}
/**
*
* @return the search
*/
public Search getSearch() {
return search;
}
/**
* Sets the search.
* @param search the search
*/
public void setSearch(final Search search) {
this.search = search;
}
/**
* Set the search value to apply to this column.
*
* @param searchValue if any, the search value to apply
*/
public void setSearchValue(final String searchValue) {
this.search.setValue(searchValue);
}
@Override
public String toString() {
return "Column{"
+ "data='" + data + '\''
+ ", name='" + name + '\''
+ ", searchable=" + searchable
+ ", orderable=" + orderable
+ ", search=" + search
+ '}';
}
}

View File

@ -1,295 +0,0 @@
package hirs.attestationca.portal.datatables;
import org.apache.commons.lang3.StringUtils;
import org.hibernate.validator.constraints.NotEmpty;
import org.springframework.util.CollectionUtils;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Represents a data table input in a jQuery DataTable.
*/
public class DataTableInput {
private static final int DEFAULT_LENGTH = 10;
/**
* Default Constructor.
*/
public DataTableInput() {
}
/**
* Constructor.
* @param draw the draw counter
* @param start the paging start indicator
* @param length the number of records in current draw
* @param search the search parameter
* @param order the orderings
* @param columns the columns of the input
*/
public DataTableInput(final Integer draw, final Integer start, final Integer length,
final Search search, final List<Order> order,
final List<Column> columns) {
this.draw = draw;
this.start = start;
this.length = length;
this.search = search;
this.order.addAll(order);
this.columns.addAll(columns);
}
/**
* Draw counter. This is used by DataTables to ensure that the Ajax returns from server-side
* processing requests are drawn in sequence by DataTables (Ajax requests are asynchronous and
* thus can return out of sequence). This is used as part of the draw return parameter (see
* below).
*/
@NotNull
@Min(0)
private int draw = 1;
/**
* Paging first record indicator. This is the start point in the current data set
* (0 index based - i.e. 0 is the first record).
*/
@NotNull
@Min(0)
private int start = 0;
/**
* Number of records that the table can display in the current draw. It is expected that the
* number of records returned will be equal to this number,
* unless the server has fewer records to return. Note that this can be -1 to indicate that
* all records should be returned (although that
* negates any benefits of server-side processing!)
*/
@NotNull
@Min(-1)
private int length = DEFAULT_LENGTH;
/**
* Global search parameter.
*/
@NotNull
private Search search = new Search();
/**
* Order parameter.
*/
@NotEmpty
private List<Order> order = new ArrayList<>();
/**
* Per-column search parameter.
*/
@NotEmpty
private List<Column> columns = new ArrayList<>();
/**
*
* @return the draw counter
*/
public int getDraw() {
return draw;
}
/**
* Sets the draw counter.
* @param draw the draw counter
*/
public void setDraw(final int draw) {
this.draw = draw;
}
/**
* Gets the start indicator.
* @return the start indicator
*/
public int getStart() {
return start;
}
/**
* Gets the start indicator.
* @param start the start indicator
*/
public void setStart(final int start) {
this.start = start;
}
/**
*
* @return the table length for the current draw
*/
public int getLength() {
return length;
}
/**
* Sets the table length for the current draw.
* @param length the table length for the current draw
*/
public void setLength(final int length) {
this.length = length;
}
/**
*
* @return the Search
*/
public Search getSearch() {
return search;
}
/**
* Sets the search.
* @param search the search
*/
public void setSearch(final Search search) {
this.search = search;
}
/**
*
* @return the orders
*/
public List<Order> getOrder() {
return order;
}
/**
* Sets the orders.
* @param order the orders
*/
public void setOrder(final List<Order> order) {
this.order.clear();
this.order.addAll(order);
}
/**
* Gets the table columns.
* @return the columns
*/
public List<Column> getColumns() {
return columns;
}
/**
* Sets the table columns.
* @param columns the columns
*/
public void setColumns(final List<Column> columns) {
this.columns.clear();
this.columns.addAll(columns);
}
/**
*
* @return a {@link Map} of {@link Column} indexed by name
*/
public Map<String, Column> getColumnsAsMap() {
Map<String, Column> map = new HashMap<String, Column>();
for (Column column : columns) {
map.put(column.getData(), column);
}
return map;
}
/**
* Find a column by its name.
*
* @param columnName the name of the column
* @return the given Column, or <code>null</code> if not found
*/
public Column getColumn(final String columnName) {
if (columnName == null) {
return null;
}
for (Column column : columns) {
if (columnName.equals(column.getData())) {
return column;
}
}
return null;
}
/**
* Add a new column.
*
* @param columnName the name of the column
* @param searchable whether the column is searchable or not
* @param orderable whether the column is orderable or not
* @param searchValue if any, the search value to apply
*/
public void addColumn(final String columnName, final boolean searchable,
final boolean orderable, final String searchValue) {
this.columns.add(new Column(columnName, "", searchable, orderable,
new Search(searchValue, false)));
}
/**
* Add an order on the given column.
*
* @param columnName the name of the column
* @param ascending whether the sorting is ascending or descending
*/
public void addOrder(final String columnName, final boolean ascending) {
if (columnName == null) {
return;
}
for (int i = 0; i < columns.size(); i++) {
if (!columnName.equals(columns.get(i).getData())) {
continue;
}
order.add(new Order(i, ascending));
}
}
/**
* Gets the order column name, given the order ordinal value.
* @return the order column name
*/
public String getOrderColumnName() {
// attempt to get the column property based on the order index.
String orderColumnName = "id";
List<Order> orders = getOrder();
if (!CollectionUtils.isEmpty(orders)) {
int orderColumnIndex = orders.get(0).getColumn();
final Column column = getColumns().get(orderColumnIndex);
// use the column's name as the order field for hibernate if set,
// otherwise, use the columns' data field
if (StringUtils.isNotEmpty(column.getName())) {
orderColumnName = column.getName();
} else {
orderColumnName = column.getData();
}
}
return orderColumnName;
}
/**
* Generates a string for this object.
* @return the string
*/
@Override
public String toString() {
return "DataTableInput{"
+ "draw=" + draw
+ ", start=" + start
+ ", length=" + length
+ ", search=" + search
+ ", order=" + order
+ ", columns=" + columns
+ '}';
}
}

View File

@ -1,9 +1,13 @@
package hirs.attestationca.portal.datatables;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import hirs.FilteredRecordsList;
/**
* A Wrapper for Data Table JSON responses. Allows Spring to serialize a data object with additional
@ -11,30 +15,26 @@ import hirs.FilteredRecordsList;
*
* @param <T> the type of object that is being wrapped.
*/
@NoArgsConstructor(access = AccessLevel.PUBLIC)
public final class DataTableResponse<T> {
private List<T> data = new LinkedList<T>();
@Getter @Setter
private int draw;
private long recordsTotal;
private long recordsFiltered;
/**
* Default constructor.
*/
public DataTableResponse() {
}
@Getter @Setter
private long recordsTotal, recordsFiltered;
/**
* Builds a data table response using a FilteredRecordList.
*
* @param recordList the filtered record list
* @param inputQuery the data table input (used for draw)
*/
public DataTableResponse(final FilteredRecordsList<T> recordList,
final DataTableInput inputQuery) {
this(recordList, inputQuery.getDraw(),
recordList.getRecordsTotal(), recordList.getRecordsFiltered());
}
// public DataTableResponse(final FilteredRecordsList<T> recordList,
// final DataTableInput inputQuery) {
// this(recordList, inputQuery.getDraw(),
// recordList.getRecordsTotal(), recordList.getRecordsFiltered());
// }
/**
* Constructs a data table response using the specified data with the data table specific
@ -55,6 +55,7 @@ public final class DataTableResponse<T> {
/**
* Gets the data table data.
*
* @return the data
*/
public List<T> getData() {
@ -63,58 +64,11 @@ public final class DataTableResponse<T> {
/**
* Sets the data table data.
*
* @param data the data
*/
public void setData(final List<T> data) {
this.data.clear();
this.data.addAll(data);
}
/**
* Gets the table draw index of the table.
* @return the draw index
*/
public int getDraw() {
return draw;
}
/**
* Sets the table draw index of the table.
* @param draw the draw index
*/
public void setDraw(final int draw) {
this.draw = draw;
}
/**
* Gets the total records.
* @return the total records count
*/
public long getRecordsTotal() {
return recordsTotal;
}
/**
* Sets the total record count.
* @param recordsTotal the total records count
*/
public void setRecordsTotal(final long recordsTotal) {
this.recordsTotal = recordsTotal;
}
/**
* Gets the total filtered record count.
* @return the total filtered record count
*/
public long getRecordsFiltered() {
return recordsFiltered;
}
/**
* Sets the total filtered record count.
* @param recordsFiltered the total filtered record count
*/
public void setRecordsFiltered(final long recordsFiltered) {
this.recordsFiltered = recordsFiltered;
}
}

View File

@ -1,45 +0,0 @@
package hirs.attestationca.portal.datatables;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import java.util.Map;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.http.MediaType;
import org.springframework.web.servlet.view.AbstractUrlBasedView;
/**
* Serializes the DataTableResponse from the view as JSON and writes it to the HTTP response.
*
*/
public class DataTableView extends AbstractUrlBasedView {
private static final Gson GSON = new GsonBuilder().create();
private static final String MODEL_FIELD;
static {
final String name = DataTableResponse.class.getSimpleName();
MODEL_FIELD = name.substring(0, 1).toLowerCase() + name.substring(1);
}
/**
* Serializes the DataTableResponse from the view as JSON and writes it to the HTTP response.
*
* @param model combined output Map (never {@code null}), with dynamic values taking precedence
* over static attributes
* @param request current HTTP request
* @param response current HTTP response
* @throws Exception if rendering failed
*/
@Override
protected void renderMergedOutputModel(
final Map<String, Object> model,
final HttpServletRequest request,
final HttpServletResponse response) throws Exception {
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
DataTableResponse dataTable = (DataTableResponse) model.get(MODEL_FIELD);
ServletOutputStream out = response.getOutputStream();
String json = GSON.toJson(dataTable);
out.print(json);
}
}

View File

@ -1,111 +0,0 @@
package hirs.attestationca.portal.datatables;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
/**
* Represents a column ordering with regards to a jQuery DataTable.
*/
public class Order {
/**
* Constructor.
*/
public Order() {
}
/**
* Constructor.
* @param column the column index
* @param dir the order direction
*/
public Order(final int column, final String dir) {
this.column = column;
this.dir = dir;
}
/**
* Constructor.
* @param column the column index
* @param isAscending true if ascending order
*/
public Order(final int column, final boolean isAscending) {
this.column = column;
if (isAscending) {
this.dir = "asc";
} else {
this.dir = "desc";
}
}
/**
* Column to which ordering should be applied. This is an index reference
* to the columns array of information that is also submitted to the server.
*/
@NotNull
@Min(0)
private int column;
/**
* Ordering direction for this column. It will be asc or desc to indicate ascending ordering or
* descending ordering, respectively.
*/
@NotNull
@Pattern(regexp = "(desc|asc)")
private String dir;
/**
* Gets the column index.
* @return the column index
*/
public int getColumn() {
return column;
}
/**
* Sets the column index.
* @param column the column index
*/
public void setColumn(final int column) {
this.column = column;
}
/**
* Gets the direction order.
* @return the direction order
*/
public String getDir() {
return dir;
}
/**
* Sets the direction order.
* @param dir the direction order
*/
public void setDir(final String dir) {
this.dir = dir;
}
/**
*
* @return true if ascending order, false otherwise.
*/
public boolean isAscending() {
if (dir.equalsIgnoreCase("asc")) {
return true;
}
return false;
}
@Override
public String toString() {
return "Order{"
+ "column=" + column
+ ", dir='" + dir + '\''
+ '}';
}
}

View File

@ -1,78 +0,0 @@
package hirs.attestationca.portal.datatables;
import org.hibernate.Criteria;
import org.springframework.util.CollectionUtils;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import hirs.FilteredRecordsList;
import hirs.persist.CriteriaModifier;
import hirs.persist.OrderedListQuerier;
/**
* A class to adapt the Javascript DataTable java class abstractions to the DBManager's getting
* of ordered lists.
* @param <T> The type of object to query
*/
public final class OrderedListQueryDataTableAdapter<T> {
private OrderedListQueryDataTableAdapter() {
// do not construct
}
/**
* Gets the ordered list of records using a default, no-op criteria modifier.
* @param clazz the type of objects to query for
* @param dbManager the db manager to execute the actual query
* @param dataTableInput the JS DataTable query abstraction
* @param orderColumnName the name of the column (java object field name) to query on
* @param <T> the parameter type
* @return the filtered record list
*/
public static <T> FilteredRecordsList<T> getOrderedList(final Class<? extends T> clazz,
final OrderedListQuerier<T> dbManager,
final DataTableInput dataTableInput,
final String orderColumnName) {
CriteriaModifier defaultModifier = new CriteriaModifier() {
@Override
public void modify(final Criteria criteria) {
// Do nothing
}
};
return getOrderedList(clazz, dbManager, dataTableInput, orderColumnName, defaultModifier);
}
/**
* Gets the ordered list of records.
* @param clazz the type of objects to query for
* @param dbManager the db manager to execute the actual query
* @param dataTableInput the JS DataTable query abstraction
* @param orderColumnName the name of the column (java object field name) to query on
* @param criteriaModifier the criteria modifier
* @param <T> the parameter type
* @return the filtered record list
*/
public static <T> FilteredRecordsList<T> getOrderedList(final Class<? extends T> clazz,
final OrderedListQuerier<T> dbManager, final DataTableInput dataTableInput,
final String orderColumnName,
final CriteriaModifier criteriaModifier) {
Map<String, Boolean> searchableColumnMap = new HashMap<>();
for (Column column : dataTableInput.getColumns()) {
searchableColumnMap.put(column.getData(), column.isSearchable());
}
List<Order> orders = dataTableInput.getOrder();
boolean isAscending = true;
if (!CollectionUtils.isEmpty(orders)) {
isAscending = orders.get(0).isAscending();
}
return dbManager.getOrderedList(clazz, orderColumnName, isAscending,
dataTableInput.getStart(), dataTableInput.getLength(),
dataTableInput.getSearch().getValue(),
searchableColumnMap, criteriaModifier);
}
}

View File

@ -1,89 +0,0 @@
package hirs.attestationca.portal.datatables;
import javax.validation.constraints.NotNull;
/**
* Represents a jQuery DataTables search parameter.
*/
public class Search {
/**
* Default Constructor.
*/
public Search() {
}
/**
* Constructor for a non-regex search.
* @param value the search value
*/
public Search(final String value) {
this(value, false);
}
/**
* Constructor.
* @param value the search value
* @param regex the search regex
*/
public Search(final String value, final boolean regex) {
this.value = value;
this.regex = regex;
}
/**
* Global search value. To be applied to all columns which have searchable as true.
*/
@NotNull
private String value = "";
/**
* true if the global filter should be treated as a regular expression for advanced searching,
* false otherwise. Note that normally server-side processing scripts will not perform regular
* expression searching for performance reasons on large data sets,
* but it is technically possible and at the discretion of your script.
*/
@NotNull
private boolean regex;
/**
*
* @return the global search value, applied to all columns.
*/
public String getValue() {
return value;
}
/**
* Sets the global search value.
* @param value the global search value
*/
public void setValue(final String value) {
this.value = value;
}
/**
*
* @return true if search should be treated as a regex, false otherwise
*/
public boolean isRegex() {
return regex;
}
/**
* Sets the regex flag.
* @param regex true if the search should be treated as a regex, false otherwise
*/
public void setRegex(final boolean regex) {
this.regex = regex;
}
@Override
public String toString() {
return "Search{"
+ "value='" + value + '\''
+ ", regex=" + regex
+ '}';
}
}

View File

@ -1,9 +0,0 @@
/**
* This packages provides classes to serve jQuery dataTable requests.
*
* DataTableInput, Column, Order, and Search from:
* https://github.com/darrachequesne/spring-data-jpa-datatables
*
* The rest of the package was unnecessary and introduced dependency conflicts.
*/
package hirs.attestationca.portal.datatables;

View File

@ -1,20 +1,26 @@
package hirs.data.persist;
package hirs.attestationca.portal.entity;
import jakarta.persistence.Column;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.MappedSuperclass;
import lombok.Getter;
import lombok.ToString;
import org.hibernate.annotations.ColumnDefault;
import org.hibernate.annotations.Generated;
import org.hibernate.annotations.GenerationTime;
import java.io.Serializable;
import java.util.Date;
import java.util.UUID;
import javax.persistence.Column;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;
import org.hibernate.annotations.GenericGenerator;
import org.hibernate.annotations.Type;
/**
* An abstract database entity.
*/
@ToString
@MappedSuperclass
public abstract class AbstractEntity {
public abstract class AbstractEntity implements Serializable {
/**
* static value for the length of a status message for objects that
@ -24,13 +30,14 @@ public abstract class AbstractEntity {
@Id
@Column(name = "id")
@GeneratedValue(generator = "uuid2")
@GenericGenerator(name = "uuid2", strategy = "uuid2")
@Type(type = "uuid-char")
@GeneratedValue(generator = "uuid2", strategy= GenerationType.AUTO)
@Getter
private UUID id;
@Column (name = "create_time")
private final Date createTime = new Date();
@ColumnDefault(value = "CURRENT_TIMESTAMP")
@Generated(GenerationTime.INSERT)
private Date createTime;// = new Date();
/**
* Default empty constructor is required for Hibernate. It is protected to
@ -40,28 +47,19 @@ public abstract class AbstractEntity {
super();
}
/**
* Returns the unique ID associated with this entity.
*
* @return unique ID
*/
public UUID getId() {
return id;
}
/**
* Returns the creation time of this entity.
*
* @return creation time
*/
public final Date getCreateTime() {
public Date getCreateTime() {
return (Date) createTime.clone();
}
/**
* Reset the creation time to the current time.
*/
public final void resetCreateTime() {
public void resetCreateTime() {
createTime.setTime(new Date().getTime());
}
@ -86,14 +84,4 @@ public abstract class AbstractEntity {
}
return this.hashCode() == obj.hashCode();
}
@Override
public String toString() {
try {
return String.format("UUID=%s, createTime=%s",
getId(), getCreateTime().toString());
} catch (NullPointerException npEx) {
return "";
}
}
}

View File

@ -0,0 +1,88 @@
package hirs.attestationca.portal.entity;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.AccessLevel;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
/**
* The <code>Appraiser</code> class represents an appraiser that can appraise a <code>Report</code>.
* <code>Appraiser</code>s are invoked to validate the integrity of client's platform. An
* <code>Appraiser</code> does this by examining a <code>Report</code> sent from the client's
* machine.
* <p>
* Supported <code>Report</code> types are kept track of in three ways: <ul> <li>The type of report
* received for appraisal is getAppraiseReportType() (e.g. the <code>DeviceInfoAppraiser</code>
* takes in a <code>DeviceInfoReport</code> and the <code>TPMAppraiser</code> takes in an
* <code>IntegrityReport</code>)</li> <li>The type requested in getReportRequest is
* getRequestReportType(). This tends to be the specific report type for that type of appraiser
* (e.g. the <code>IMAAppraiser</code> requests an <code>IMAReport</code> and the
* <code>TPMAppraiser</code> requests a <code>TPMReport</code>)</li> <li>The set of types this
* appraiser relies on extracting from the top-level report is getRequiredReportTypes() (e.g. if the
* top-level report is <code>IntegrityReport</code> then the <code>IMAAppraiser</code> needs to
* extract both a <code>DeviceInfoReport</code> and a <code>IMAReport</code> from the
* <code>IntegrityReport</code>)</li> </ul>
*/
@Entity
@Table(name = "Appraiser")
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@ToString
@EqualsAndHashCode(callSuper = false)
public class Appraiser {
/**
* Name set for every instance of <code>TPMAppraiser</code>.
*/
public static final String TPM_NAME = "TPM Appraiser";
/**
* Name set for every instance of <code>SupplyChainAppraiser</code>.
*/
public static final String SC_NAME = "Supply Chain Appraiser";
/**
* Name set for every instance of <code>IMAAppraiser</code>.
*/
public static final String IMA_NAME = "IMA Appraiser";
/**
* Name set for every instance of <code>HIRSAppraiser</code>.
*/
public static final String HIRS_NAME = "HIRS Appraiser";
/**
* Name set for every instance of <code>DeviceInfoAppraiser</code>.
*/
public static final String DI_NAME = "Device Info Appraiser";
@Getter
@ToString.Exclude
@EqualsAndHashCode.Exclude
@Id
@Column(name = "Appraiser_ID")
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@Getter
@Setter
@Column(nullable = false, unique = true)
private String name;
/**
* Creates a new <code>Appraiser</code> with the specified name. The name should be universally
* unique as this is how other components will identify <code>Appraiser</code>s. Web portals,
* for instance, could display a list of <code>Appraiser</code> names to display which
* <code>Appraiser</code>s are available.
* <p>
* The name will be tested for uniqueness when it is added to a repository. It is not tested for
* uniqueness in the class.
*
* @param name unique name
*/
public Appraiser(final String name) {
this.name = name;
}
}

View File

@ -1,12 +1,17 @@
package hirs.data.persist;
package hirs.attestationca.portal.entity;
import jakarta.persistence.Column;
import jakarta.persistence.MappedSuperclass;
import lombok.Getter;
import lombok.ToString;
import javax.persistence.Column;
import javax.persistence.MappedSuperclass;
import java.util.Date;
/**
* An abstract archivable entity that can be deleted.
*/
@ToString
@Getter
@MappedSuperclass
public abstract class ArchivableEntity extends AbstractEntity {
@ -87,41 +92,4 @@ public abstract class ArchivableEntity extends AbstractEntity {
}
return false;
}
/**
* Returns the timestamp of when the entity was archived if applicable. If the
* entity has not been resolved, then null is returned.
*
* @return archivedTime
* If entity was archived, timestamp of the occurrence, null otherwise.
*/
public final Date getArchivedTime() {
if (archivedTime == null) {
return null;
} else {
return (Date) archivedTime.clone();
}
}
/**
* Returns the description of the action taken during resolution. The description can be null.
*
* @return
* description string of the action taken during resolution
*/
public final String getArchivedDescription() {
return this.archivedDescription;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(super.toString());
if (archivedTime != null) {
sb.append(String.format(", archivedTime=%s, archiveDescription=%s",
archivedTime.toString(), archivedDescription));
}
return sb.toString();
}
}

View File

@ -1,11 +1,10 @@
package hirs.data.persist;
package hirs.attestationca.portal.entity;
import javax.persistence.Access;
import javax.persistence.AccessType;
import javax.persistence.Entity;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.Table;
import jakarta.persistence.Access;
import jakarta.persistence.AccessType;
import jakarta.persistence.Inheritance;
import jakarta.persistence.InheritanceType;
import jakarta.persistence.MappedSuperclass;
/**
* The <code>Policy</code> class represents a policy. This is an abstract class
@ -14,12 +13,19 @@ import javax.persistence.Table;
* <code>Baseline</code> at the very least. A <code>Policy</code> is identified
* by its name, so the name for a <code>Policy</code> must be unique.
*/
@Entity
@Table(name = "Policy")
@Inheritance(strategy = InheritanceType.JOINED)
@Access(AccessType.FIELD)
@MappedSuperclass
public abstract class Policy extends UserDefinedEntity {
/**
* Default empty constructor is required for Hibernate. It is protected to
* prevent code from calling it directly.
*/
protected Policy() {
super();
}
/**
* Creates a new <code>Policy</code> with the specified name.
*
@ -43,14 +49,6 @@ public abstract class Policy extends UserDefinedEntity {
super(name, description);
}
/**
* Default empty constructor is required for Hibernate. It is protected to
* prevent code from calling it directly.
*/
protected Policy() {
super();
}
/**
* Returns true if this object has been persisted. Used in determining whether
* an Appraiser should request the full Policy (and baselines) for appraisal
@ -62,12 +60,13 @@ public abstract class Policy extends UserDefinedEntity {
}
/**
* When {@link Policy} are serialized to be sent to the browser, this can be used
* to determine the type of {@link Policy}.
* When {@link hirs.attestationca.portal.entity.Policy} are serialized to be sent to the browser, this can be used
* to determine the type of {@link hirs.attestationca.portal.entity.Policy}.
*
* @return The class name for the {@link Policy}
* @return The class name for the {@link hirs.attestationca.portal.entity.Policy}
*/
public String getType() {
return this.getClass().getSimpleName();
}
}

View File

@ -0,0 +1,47 @@
package hirs.attestationca.portal.entity;
import jakarta.persistence.Column;
import jakarta.persistence.MappedSuperclass;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
/**
* An abstract archivable entity that can be given a user-defined name and description.
*/
@Getter
@Setter
@EqualsAndHashCode(callSuper = false)
@AllArgsConstructor
@MappedSuperclass
public abstract class UserDefinedEntity extends ArchivableEntity {
@Column(nullable = false, unique = true)
private String name;
@ToString.Exclude
@EqualsAndHashCode.Exclude
@Column(nullable = false, unique = false)
private String description = "";
/**
* Default empty constructor is required for Hibernate. It is protected to
* prevent code from calling it directly.
*/
protected UserDefinedEntity() {
super();
}
/**
* Creates a new entity with the specified name.
*
* @param name name
*/
public UserDefinedEntity(final String name) {
this(name, "");
}
}

View File

@ -0,0 +1,15 @@
package hirs.attestationca.portal.entity.manager;
import hirs.attestationca.portal.entity.userdefined.Device;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.UUID;
@Repository
public interface DeviceRepository extends CrudRepository<Device, UUID> {
List<Device> findByName(String deviceName);
}

View File

@ -0,0 +1,12 @@
package hirs.attestationca.portal.entity.manager;
import hirs.attestationca.portal.entity.userdefined.SupplyChainSettings;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.UUID;
@Repository
public interface SettingsRepository extends JpaRepository<SupplyChainSettings, UUID> {
SupplyChainSettings findByName(String name);
}

View File

@ -0,0 +1,4 @@
/**
* This package has objects for hibernate entity.
*/
package hirs.attestationca.portal.entity;

View File

@ -0,0 +1,63 @@
package hirs.attestationca.portal.entity.userdefined;
import hirs.attestationca.portal.entity.AbstractEntity;
import hirs.attestationca.portal.enums.AppraisalStatus;
import hirs.attestationca.portal.enums.HealthStatus;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.Table;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import java.sql.Timestamp;
@Entity
@Table(name = "Device")
@Getter
@Setter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
public class Device extends AbstractEntity {
@Column(name = "name", unique = true)
private String name;
// @OneToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER,
// optional = true, orphanRemoval = true)
// private DeviceInfoReport deviceInfo;
@Column
@Enumerated(EnumType.ORDINAL)
private HealthStatus healthStatus;
@Column
@Enumerated(EnumType.ORDINAL)
private AppraisalStatus.Status supplyChainValidationStatus;
/**
* Time stamp for the report.
*/
@Column(name = "last_report_timestamp")
private Timestamp lastReportTimestamp;
@Column(name = "is_state_overridden")
private boolean isStateOverridden;
@Column(name = "state_override_reason")
private String overrideReason;
@Column(name = "summary_id")
private String summaryId;
public String toString() {
return String.format("Device Name: %s%nStatus: %s%nSummary: %s",
name, healthStatus.getStatus(),
// supplyChainValidationStatus.toString(),
summaryId);
}
}

View File

@ -0,0 +1,121 @@
package hirs.attestationca.portal.entity.userdefined;
import hirs.attestationca.portal.entity.UserDefinedEntity;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Table;
import lombok.Getter;
import lombok.Setter;
/**
* Class represents Supply Chain policy. Supply Chain Policy identifies the methods in
* SupplyChainValidator that should be used in order to validate a supply chain.
* By default, the policy does not enable any validations.
*/
@Table(name = "SupplyChainSettings")
@Getter
@Setter
@Entity
public class SupplyChainSettings extends UserDefinedEntity {
/**
* Name of the default Supply Chain Policy.
*/
public static final String DEFAULT_POLICY = "Default Supply Chain Policy";
/**
* Number of days in 10 years.
*/
public static final String TEN_YEARS = "3651";
/**
* Number of days in 1 year.
*/
public static final String YEAR = "365";
@Column(nullable = false, columnDefinition = "boolean default false")
private boolean ecValidationEnabled = false;
@Column(nullable = false, columnDefinition = "boolean default false")
private boolean pcValidationEnabled = false;
@Column(nullable = false, columnDefinition = "boolean default false")
private boolean pcAttributeValidationEnabled = false;
@Column(nullable = false, columnDefinition = "boolean default false")
private boolean firmwareValidationEnabled = false;
@Column(nullable = false, columnDefinition = "boolean default false")
private boolean utcValidationEnabled = false;
@Column(nullable = false, columnDefinition = "boolean default false")
private boolean expiredCertificateValidationEnabled = false;
@Column(nullable = false, columnDefinition = "boolean default false")
private boolean replaceEC = false;
@Column(nullable = false, columnDefinition = "boolean default true")
private boolean issueAttestationCertificate = true;
@Column(nullable = false, columnDefinition = "boolean default true")
private boolean issueDevIdCertificate = true;
@Column(nullable = false)
private String validityDays = TEN_YEARS;
@Column(nullable = false)
private String devIdValidityDays = TEN_YEARS;
@Column(nullable = false)
private String reissueThreshold = YEAR;
@Column(nullable = false)
private String devIdReissueThreshold = YEAR;
@Column(nullable = false, columnDefinition = "boolean default false")
private boolean generateOnExpiration = false;
@Column(nullable = false, columnDefinition = "boolean default false")
private boolean devIdExpirationFlag = false;
@Column(nullable = false, columnDefinition = "boolean default false")
private boolean ignoreImaEnabled = false;
@Column(nullable = false, columnDefinition = "boolean default false")
private boolean ignoretBootEnabled = false;
@Column(nullable = false, columnDefinition = "boolean default false")
private boolean linuxOs = false;
@Column(nullable = false, columnDefinition = "boolean default true")
private boolean ignoreGptEnabled = true;
@Column(nullable = false, columnDefinition = "boolean default false")
private boolean ignoreOsEvtEnabled = false;
/**
* Default constructor necessary for Hibernate.
*/
protected SupplyChainSettings() {
super();
}
/**
* Constructor used to initialize SupplyChainSettings object.
*
* @param name
* A name used to uniquely identify and reference the Supply Chain policy.
*/
public SupplyChainSettings(final String name) {
super(name);
}
/**
* Constructor used to initialize SupplyChainSettings object.
*
* @param name
* A name used to uniquely identify and reference the supply chain policy.
* @param description
* Optional description of the policy that can be added by the user
*/
public SupplyChainSettings(final String name, final String description) {
super(name, description);
}
}

View File

@ -1,4 +1,4 @@
package hirs.data.persist;
package hirs.attestationca.portal.enums;
/**
* Class to capture appraisal results and corresponding messages.
@ -30,7 +30,6 @@ public class AppraisalStatus {
}
private Status appStatus;
private String message;
private String additionalInfo;

View File

@ -1,4 +1,7 @@
package hirs.data.persist.enums;
package hirs.attestationca.portal.enums;
import java.util.Arrays;
import java.util.stream.Collectors;
/**
* <code>HealthStatus</code> is used to represent the health of a device.
@ -19,16 +22,16 @@ public enum HealthStatus {
*/
UNKNOWN("unknown");
private String status;
private String healthStatus;
/**
* Creates a new <code>HealthStatus</code> object given a String.
*
* @param status
* @param healthStatus
* "trusted", "untrusted", or "unknown"
*/
HealthStatus(final String status) {
this.status = status;
HealthStatus(final String healthStatus) {
this.healthStatus = healthStatus;
}
/**
@ -37,11 +40,18 @@ public enum HealthStatus {
* @return the status
*/
public String getStatus() {
return this.status;
return this.healthStatus;
}
@Override
public String toString() {
return getStatus();
}
public static boolean isValidStatus(final String healthStatus) {
return Arrays.stream(HealthStatus.values())
.map(HealthStatus::name)
.collect(Collectors.toSet())
.contains(healthStatus);
}
}

View File

@ -1,6 +1,6 @@
package hirs.attestationca.portal.page;
package hirs.attestationca.portal.enums;
import hirs.utils.VersionHelper;
import hirs.attestationca.portal.utils.VersionHelper;
/**
* Contains attributes required to display a portal page and its menu link.
@ -12,52 +12,10 @@ public enum Page {
*/
INDEX("HIRS Attestation CA", "Version: " + VersionHelper.getVersion(),
null, false, false, null, null),
/**
* Page to import and manage trust chains.
*/
TRUST_CHAIN("Trust Chain Management", "ic_store",
null, "certificate-request/"),
/**
* Page to display and manage endorsement key credentials.
*/
ENDORSEMENT_KEY_CREDENTIALS("Endorsement Key Certificates", "ic_vpn_key",
"first", "certificate-request/"),
/**
* Page to display and manage platform credentials.
*/
PLATFORM_CREDENTIALS("Platform Certificates", "ic_important_devices",
null, "certificate-request/"),
/**
* Page to display issued certificates.
*/
ISSUED_CERTIFICATES("Issued Certificates", "ic_library_books",
null, "certificate-request/"),
/**
* Page to display certificate validation reports.
*/
VALIDATION_REPORTS("Validation Reports", "ic_assignment", "first"),
/**
* Non-menu page to display certificate. Reachable from all certificate pages.
*/
CERTIFICATE_DETAILS("Certificate Details", "", null, true, false, null, null),
/**
* Page to display registered devices.
*/
DEVICES("Devices", "ic_devices", "first"),
/**
* Page to display RIMs.
*/
REFERENCE_MANIFESTS("Reference Integrity Manifests",
"ic_important_devices", "first"),
/**
* Non-menu page to display rims.
*/
RIM_DETAILS("Reference Integrity Manifest Details",
"", null, true, false, null, null),
/**
* Page to display RIM event digest table.
*/
RIM_DATABASE("RIM Database", "ic_important_devices", "first"),
/**
* Page that manages Attestation CA Policy.
*/
@ -221,3 +179,4 @@ public enum Page {
}
}

View File

@ -0,0 +1 @@
package hirs.attestationca.portal.enums;

View File

@ -1,626 +0,0 @@
package hirs.attestationca.portal.model;
import hirs.data.persist.SupplyChainPolicy;
/**
* PolicyPage model object to demonstrate data exchange between policy.jsp page
* form form and controller.
*/
public class PolicyPageModel {
// Variables to communicate policy settings to page
private boolean enableEcValidation;
private boolean enablePcCertificateValidation;
private boolean enablePcCertificateAttributeValidation;
private boolean enableFirmwareValidation;
private boolean issueAttestationCertificate;
private boolean issueDevIdCertificate;
private boolean generateOnExpiration;
private boolean devIdExpirationFlag;
private boolean enableIgnoreIma;
private boolean enableIgnoreTboot;
private boolean enableIgnoreGpt;
private boolean enableIgnoreOsEvt;
// Variables to get policy settings from page
private String pcValidate;
private String pcAttributeValidate;
private String ecValidate;
private String fmValidate;
private String attestationCertificateIssued;
private String devIdCertificateIssued;
private String generationExpirationOn;
private String devIdExpirationChecked;
private String numOfValidDays;
private String reissueThreshold;
private String devIdReissueThreshold;
private String ignoreIma;
private String ignoretBoot;
private String ignoreGpt;
private String ignoreOsEvt;
private String expirationValue;
private String devIdExpirationValue;
private String thresholdValue;
private String devIdThresholdValue;
/**
* Constructor. Sets fields from policy.
*
* @param policy The supply chain policy
*/
public PolicyPageModel(final SupplyChainPolicy policy) {
this.enableEcValidation = policy.isEcValidationEnabled();
this.enablePcCertificateValidation = policy.isPcValidationEnabled();
this.enablePcCertificateAttributeValidation = policy.isPcAttributeValidationEnabled();
this.enableFirmwareValidation = policy.isFirmwareValidationEnabled();
this.issueAttestationCertificate = policy.isIssueAttestationCertificate();
this.issueDevIdCertificate = policy.isIssueDevIdCertificate();
this.generateOnExpiration = policy.isGenerateOnExpiration();
this.devIdExpirationFlag = policy.isDevIdExpirationFlag();
this.numOfValidDays = policy.getValidityDays();
this.reissueThreshold = policy.getReissueThreshold();
this.enableIgnoreIma = policy.isIgnoreImaEnabled();
this.enableIgnoreTboot = policy.isIgnoreTbootEnabled();
this.enableIgnoreGpt = policy.isIgnoreGptEnabled();
this.enableIgnoreOsEvt = policy.isIgnoreOsEvtEnabled();
this.expirationValue = policy.getValidityDays();
this.thresholdValue = policy.getReissueThreshold();
this.devIdExpirationValue = policy.getDevIdValidityDays();
this.devIdReissueThreshold = policy.getDevIdReissueThreshold();
this.devIdThresholdValue = policy.getDevIdReissueThreshold();
}
/**
* Default constructor required by Spring.
*/
public PolicyPageModel() {
}
/**
* Gets the EC Validation state.
*
* @return the validation state.
*/
public boolean getEnableEcValidation() {
return enableEcValidation;
}
/**
* Gets the Platform Certificate Validation state.
*
* @return the validation state.
*/
public boolean getEnablePcCertificateValidation() {
return enablePcCertificateValidation;
}
/**
* Gets the Platform Certificate AttributeValidation state.
*
* @return the validation state.
*/
public boolean getEnablePcCertificateAttributeValidation() {
return enablePcCertificateAttributeValidation;
}
/**
* Gets the Firmware Validation state.
*
* @return the validation state.
*/
public boolean getEnableFirmwareValidation() {
return enableFirmwareValidation;
}
/**
* Gets the Attestation Certificate issued State.
*
* @return the issued state.
*/
public boolean isIssueAttestationCertificate() {
return issueAttestationCertificate;
}
/**
* Gets the Dev ID Certificate issued State.
*
* @return the issued state.
*/
public boolean isIssueDevIdCertificate() {
return issueDevIdCertificate;
}
/**
* Gets the state of generating a certificate.
*
* @return true or false
*/
public boolean isGenerateOnExpiration() {
return generateOnExpiration;
}
/**
* Gets the Enable Ignore IMA state.
* @return the validation state.
*/
public boolean getEnableIgnoreIma() {
return enableIgnoreIma;
}
/**
* Gets the Enable Ignore TBoot state.
* @return the validation state.
*/
public boolean getEnableIgnoreTboot() {
return enableIgnoreTboot;
}
/**
* Gets the Enable Ignore GPT state.
* @return the validation state.
*/
public boolean getEnableIgnoreGpt() {
return enableIgnoreGpt;
}
/**
* Gets the Enable Ignore Os Events state.
* @return the validation state.
*/
public boolean getEnableIgnoreOsEvt() {
return enableIgnoreOsEvt;
}
/**
* Gets the EC Validation value.
*
* @return the model string representation of this field (checked or unchecked)
*/
public String getEcValidate() {
return ecValidate;
}
/**
* Gets the Platform Certificate Validation value.
*
* @return the model string representation of this field (checked or unchecked)
*/
public String getPcValidate() {
return pcValidate;
}
/**
* Gets the platform certificate attribute validation value.
*
* @return the model string representation of this field (checked or unchecked)
*/
public String getPcAttributeValidate() {
return pcAttributeValidate;
}
/**
* Gets the Firmware Validation value.
*
* @return the model string representation of this field (checked or unchecked)
*/
public String getFmValidate() {
return fmValidate;
}
/**
* Gets the attestation certificate issued state.
*
* @return the model string representation of this field.
*/
public String getAttestationCertificateIssued() {
return attestationCertificateIssued;
}
/**
* Gets the DevID certificate issued state.
*
* @return the model string representation of this field.
*/
public String getDevIdCertificateIssued() {
return devIdCertificateIssued;
}
/**
* Gets the number of selected valid days.
*
* @return the number of the days for validity
*/
public String getNumOfValidDays() {
return numOfValidDays;
}
/**
* Gets the number of selected threshold days.
*
* @return the number of the days for reissue
*/
public String getReissueThreshold() {
return reissueThreshold;
}
/**
* Gets the number of selected threshold days.
*
* @return the number of the days for reissue
*/
public String getDevIdReissueThreshold() {
return devIdReissueThreshold;
}
/**
* Gets the Ignore IMA validation value.
*
* @return the model string representation of this field (checked or unchecked)
*/
public String getIgnoreIma() {
return ignoreIma;
}
/**
* Gets the Ignore TBoot validation value.
*
* @return the model string representation of this field (checked or unchecked)
*/
public String getIgnoretBoot() {
return ignoretBoot;
}
/**
* Gets the Ignore GPT validation value.
*
* @return the model string representation of this field (checked or unchecked)
*/
public String getIgnoreGpt() {
return ignoreGpt;
}
/**
* Gets the Ignore Os Evt validation value.
*
* @return the model string representation of this field (checked or unchecked)
*/
public String getIgnoreOsEvt() {
return ignoreOsEvt;
}
/**
* Sets the EC Validation state.
*
* @param enableEcValidation true if performing validation, false otherwise
*/
public void setEnableEcValidation(final boolean enableEcValidation) {
this.enableEcValidation = enableEcValidation;
}
/**
* Sets the Platform Certificate Validation state.
*
* @param enablePcCertificateValidation true if performing validation, false otherwise
*/
public void setEnablePcCertificateValidation(final boolean enablePcCertificateValidation) {
this.enablePcCertificateValidation = enablePcCertificateValidation;
}
/**
* Sets the Platform Certificate Attribute Validation state.
*
* @param enablePcCertificateAttributeValidation true if performing validation, false otherwise
*/
public void setEnablePcCertificateAttributeValidation(
final boolean enablePcCertificateAttributeValidation) {
this.enablePcCertificateAttributeValidation = enablePcCertificateAttributeValidation;
}
/**
* Sets the Firmware Validation state.
*
* @param enableFirmwareValidation true if performing validation, false otherwise
*/
public void setEnableFirmwareValidation(final boolean enableFirmwareValidation) {
this.enableFirmwareValidation = enableFirmwareValidation;
}
/**
* Sets the Attestation Certificate Issued state.
*
* @param issueAttestationCertificate true if generating Certificates.
*/
public void setIssueAttestationCertificate(final boolean issueAttestationCertificate) {
this.issueAttestationCertificate = issueAttestationCertificate;
}
/**
* Sets the Dev ID Certificate Issued state.
*
* @param issueDevIdCertificate true if generating Certificates.
*/
public void setIssueDevIdCertificate(final boolean issueDevIdCertificate) {
this.issueDevIdCertificate = issueDevIdCertificate;
}
/**
* Setter for the state of generating a certificate.
*
* @param generateOnExpiration true or false
*/
public void setGenerateOnExpiration(final boolean generateOnExpiration) {
this.generateOnExpiration = generateOnExpiration;
}
/**
* Sets the Enable Ignore IMA state.
*
* @param enableIgnoreIma true if performing validation, false otherwise
*/
public void setEnableIgnoreIma(final boolean enableIgnoreIma) {
this.enableIgnoreIma = enableIgnoreIma;
}
/**
* Sets the Enable Ignore TBoot state.
*
* @param enableIgnoreTboot true if performing validation, false otherwise
*/
public void setEnableIgnoreTboot(final boolean enableIgnoreTboot) {
this.enableIgnoreTboot = enableIgnoreTboot;
}
/**
* Sets the Enable Ignore GPT state.
*
* @param enableIgnoreGpt true if performing validation, false otherwise
*/
public void setEnableIgnoreGpt(final boolean enableIgnoreGpt) {
this.enableIgnoreGpt = enableIgnoreGpt;
}
/**
* Sets the Enable Ignore Os Events state.
*
* @param enableIgnoreOsEvt true if performing validation, false otherwise
*/
public void setEnableIgnoreOsEvt(final boolean enableIgnoreOsEvt) {
this.enableIgnoreOsEvt = enableIgnoreOsEvt;
}
/**
* Sets the Platform Certificate Validation state.
*
* @param pcValidate "checked" if enabling validation, false otherwise
*/
public void setPcValidate(final String pcValidate) {
this.pcValidate = pcValidate;
}
/**
* Sets the EC Validation state.
*
* @param ecValidate "checked" if enabling validation, false otherwise
*/
public void setEcValidate(final String ecValidate) {
this.ecValidate = ecValidate;
}
/**
* Sets the PC Attribute Validation state.
*
* @param pcAttributeValidate "checked" if enabling validation, false otherwise
*/
public void setPcAttributeValidate(final String pcAttributeValidate) {
this.pcAttributeValidate = pcAttributeValidate;
}
/**
* Sets the Firmware state.
*
* @param fmValidate "checked" if enabling validation, false otherwise
*/
public void setFmValidate(final String fmValidate) {
this.fmValidate = fmValidate;
}
/**
* Sets the Issued Attestation Certificate state.
*
* @param attestationCertificateIssued "checked" if generating certificates.
*/
public void setAttestationCertificateIssued(
final String attestationCertificateIssued) {
this.attestationCertificateIssued = attestationCertificateIssued;
}
/**
* Sets the Issued DevID Certificate state.
*
* @param devIdCertificateIssued "checked" if generating certificates.
*/
public void setDevIdCertificateIssued(final String devIdCertificateIssued) {
this.devIdCertificateIssued = devIdCertificateIssued;
}
/**
* Gets the attestation certificate issued state.
*
* @return the model string representation of this field.
*/
public String getGenerationExpirationOn() {
return generationExpirationOn;
}
/**
* Sets the generation expiration state.
*
* @param generationExpirationOn "checked" if generating expiration is on.
*/
public void setGenerationExpirationOn(
final String generationExpirationOn) {
this.generationExpirationOn = generationExpirationOn;
}
/**
* Gets the attestation certificate issued state.
*
* @return the model string representation of this field.
*/
public String getDevIdExpirationChecked() {
return devIdExpirationChecked;
}
/**
* Sets the generation expiration state.
*
* @param devIdExpirationChecked "checked" if generating expiration is on.
*/
public void setDevIdExpirationChecked(
final String devIdExpirationChecked) {
this.devIdExpirationChecked = devIdExpirationChecked;
}
/**
* Gets the DevID certificate issued state.
*
* @return the model string representation of this field.
*/
public boolean getDevIdExpirationFlag() {
return devIdExpirationFlag;
}
/**
* Sets the generation expiration state.
*
* @param devIdExpirationFlag "checked" if generating expiration is on.
*/
public void setDevIdExpirationFlag(final boolean devIdExpirationFlag) {
this.devIdExpirationFlag = devIdExpirationFlag;
}
/**
* Sets the Ignore IMA state.
*
* @param ignoreIma "checked" if enabling validation, false otherwise
*/
public void setIgnoreIma(final String ignoreIma) {
this.ignoreIma = ignoreIma;
}
/**
* Sets the Ignore Tboot state.
*
* @param ignoretBoot "checked" if enabling validation, false otherwise
*/
public void setIgnoretBoot(final String ignoretBoot) {
this.ignoretBoot = ignoretBoot;
}
/**
* Sets the Ignore GPT state.
*
* @param ignoreGpt "checked" if enabling validation, false otherwise
*/
public void setIgnoreGpt(final String ignoreGpt) {
this.ignoreGpt = ignoreGpt;
}
/**
* Sets the Ignore Os Events state.
*
* @param ignoreOsEvt "checked" if enabling validation, false otherwise
*/
public void setIgnoreOsEvt(final String ignoreOsEvt) {
this.ignoreOsEvt = ignoreOsEvt;
}
/**
* Getter for the expiration value.
* @return the value
*/
public String getExpirationValue() {
return expirationValue;
}
/**
* Setter for the expiration value.
* @param expirationValue string value
*/
public void setExpirationValue(final String expirationValue) {
this.expirationValue = expirationValue;
}
/**
* Getter for the DevID expiration value.
* @return the value
*/
public String getDevIdExpirationValue() {
return devIdExpirationValue;
}
/**
* Setter for the DevID expiration value.
* @param devIdExpirationValue string value
*/
public void setDevIdExpirationValue(final String devIdExpirationValue) {
this.devIdExpirationValue = devIdExpirationValue;
}
/**
* Getter for the expiration value.
* @return the thresholdValue
*/
public String getThresholdValue() {
return thresholdValue;
}
/**
* Setter for the expiration value.
* @param thresholdValue string value
*/
public void setThresholdValue(final String thresholdValue) {
this.thresholdValue = thresholdValue;
}
/**
* Getter for the expiration value.
* @return the devIdThresholdValue
*/
public String getDevIdThresholdValue() {
return devIdThresholdValue;
}
/**
* Setter for the expiration value.
* @param devIdThresholdValue string value
*/
public void setDevIdThresholdValue(final String devIdThresholdValue) {
this.devIdThresholdValue = devIdThresholdValue;
}
@Override
public String toString() {
return "PolicyPageModel{"
+ "enableEcValidation=" + enableEcValidation
+ ", enablePcCertificateValidation=" + enablePcCertificateValidation
+ ", enablePcCertificateAttributeValidation="
+ enablePcCertificateAttributeValidation
+ ", enableFirmwareValidation=" + enableFirmwareValidation
+ ", issueAttestationCertificate=" + issueAttestationCertificate
+ ", issueDevIdCertificate=" + issueDevIdCertificate
+ ", generateOnExpiration=" + generateOnExpiration
+ ", numOfValidDays=" + numOfValidDays
+ ", reissueThreshold=" + reissueThreshold
+ ", enableIgnoreIma=" + enableIgnoreIma
+ ", enableIgnoreTboot=" + enableIgnoreTboot
+ ", enableIgnoreGpt=" + enableIgnoreGpt
+ ", enableIgnoreOsEvt=" + enableIgnoreOsEvt
+ ", expirationValue=" + expirationValue
+ ", thresholdValue=" + thresholdValue
+ ", devIdExpirationValue=" + devIdExpirationValue
+ ", devIdReissueThreshold=" + devIdReissueThreshold
+ ", devIdThresholdValue=" + devIdThresholdValue
+ "}";
}
}

View File

@ -1,4 +0,0 @@
/**
* This packages provides data model classes for the HIRS Attestation CA portal.
*/
package hirs.attestationca.portal.model;

View File

@ -1,80 +0,0 @@
package hirs.attestationca.portal.page;
import hirs.attestationca.portal.datatables.DataTableView;
import hirs.attestationca.portal.persistence.PersistenceConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import org.springframework.web.servlet.view.UrlBasedViewResolver;
import java.nio.charset.StandardCharsets;
/**
* Specifies the location to scan for page controllers, view resolver for JSON data, and view
* resolver to map view names to jsp files.
*/
@Configuration
@EnableWebMvc
@ComponentScan("hirs.attestationca.portal.page.controllers")
@Import({ PersistenceConfiguration.class })
public class CommonPageConfiguration {
/**
* @return bean to resolve injected annotation.Value
* property expressions for beans.
*/
@Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
/**
* Makes all URLs that end in "dataTable" use DataTableView to serialize DataTableResponse.
*
* @return ViewResolver that uses DataTableView.
*/
@Bean
public ViewResolver dataTableViewResolver() {
UrlBasedViewResolver resolver = new UrlBasedViewResolver();
resolver.setViewClass(DataTableView.class);
resolver.setViewNames("*dataTable");
resolver.setOrder(0);
return resolver;
}
/**
* Maps view names to the appropriate jsp file.
*
* Only seems to apply to GET requests.
*
* @return a ViewResolver bean containing the mapping.
*/
@Bean
public ViewResolver pageViewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/jsp/");
resolver.setSuffix(".jsp");
return resolver;
}
/**
* Creates a Spring Resolver for Multi-part form uploads. This is required
* for spring controllers to be able to process Spring MultiPartFiles
*
* @return bean to handle multipart form requests
*/
@Bean
public CommonsMultipartResolver multipartResolver() {
CommonsMultipartResolver resolver = new CommonsMultipartResolver();
resolver.setDefaultEncoding(StandardCharsets.UTF_8.name());
return resolver;
}
}

View File

@ -1,14 +0,0 @@
package hirs.attestationca.portal.page;
import hirs.attestationca.configuration.AttestationCertificateAuthorityConfiguration;
import org.springframework.context.annotation.Import;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
/**
* Main Spring configuration class for the ACA Portal. Uses the Common page configuration,
* as well as the ACA configuration for accessing the ACA certificate.
*/
@Import({ CommonPageConfiguration.class, AttestationCertificateAuthorityConfiguration.class })
public class PageConfiguration extends WebMvcConfigurerAdapter {
}

View File

@ -1,12 +1,11 @@
package hirs.attestationca.portal.page;
import hirs.utils.BannerConfiguration;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import hirs.attestationca.portal.enums.Page;
import hirs.attestationca.portal.utils.BannerConfiguration;
import lombok.AllArgsConstructor;
import org.apache.http.client.utils.URIBuilder;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.ui.ExtendedModelMap;
import org.springframework.ui.Model;
import org.springframework.ui.ModelMap;
@ -16,12 +15,21 @@ import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import org.springframework.web.servlet.view.RedirectView;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.Map;
import java.util.Optional;
/**
* Abstract class to provide common functionality for page Controllers.
*
* @param <P> PageParams class used by the subclass.
*/
@AllArgsConstructor
public abstract class PageController<P extends PageParams> {
private static final Logger LOGGER = LogManager.getLogger(PageController.class);
/**
* Model attribute name used by initPage for the initial data passed to the page.
*/
@ -52,16 +60,6 @@ public abstract class PageController<P extends PageParams> {
private final Page page;
/**
* Constructor requiring the Page's display and routing specification.
*
* @param page The page specification for this controller.
*/
public PageController(final Page page) {
this.page = page;
}
/**
* Returns the path for the view and the data model for the page.
*
@ -118,7 +116,7 @@ public abstract class PageController<P extends PageParams> {
* @param model The model data to pass to the page.
* @param attr The request's RedirectAttributes to hold the model data.
* @return RedirectView back to the page with the specified parameters.
* @throws URISyntaxException if malformed URI
* @throws java.net.URISyntaxException if malformed URI
*/
protected final RedirectView redirectToSelf(
final P params,
@ -136,7 +134,7 @@ public abstract class PageController<P extends PageParams> {
* @param model The model data to pass to the page.
* @param attr The request's RedirectAttributes to hold the model data.
* @return RedirectView back to the page with the specified parameters.
* @throws URISyntaxException if malformed URI
* @throws java.net.URISyntaxException if malformed URI
*/
protected final RedirectView redirectTo(
final Page newPage,
@ -144,25 +142,27 @@ public abstract class PageController<P extends PageParams> {
final Map<String, ?> model,
final RedirectAttributes attr) throws URISyntaxException {
String defaultUri = "../" + newPage.getViewName();
// create uri with specified parameters
URIBuilder uri = new URIBuilder("../" + newPage.getViewName());
LOGGER.error(uri.toString());
if (params != null) {
for (Entry<String, ?> e : params.asMap().entrySet()) {
for (Map.Entry<String, ?> e : params.asMap().entrySet()) {
Object v = Optional.ofNullable(e.getValue()).orElse("");
uri.addParameter(e.getKey(), v.toString());
}
}
// create view
RedirectView redirect = new RedirectView(uri.toString());
RedirectView redirect = new RedirectView(defaultUri);
// do not put model attributes in the url
redirect.setExposeModelAttributes(false);
// add model data to forward to redirected page
if (model != null) {
for (Entry<String, ?> e : model.entrySet()) {
for (Map.Entry<String, ?> e : model.entrySet()) {
attr.addFlashAttribute(e.getKey(), e.getValue());
}
}

View File

@ -0,0 +1,80 @@
package hirs.attestationca.portal.page;
import hirs.attestationca.portal.entity.userdefined.SupplyChainSettings;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
/**
* PolicyPage model object to demonstrate data exchange between policy.jsp page
* form form and controller.
*/
@Setter
@Getter
@ToString
@NoArgsConstructor
public class PolicyPageModel {
// Variables to communicate policy settings to page
private boolean enableEcValidation;
private boolean enablePcCertificateValidation;
private boolean enablePcCertificateAttributeValidation;
private boolean enableFirmwareValidation;
private boolean issueAttestationCertificate;
private boolean issueDevIdCertificate;
private boolean generateOnExpiration;
private boolean devIdExpirationFlag;
private boolean enableIgnoreIma;
private boolean enableIgnoreTboot;
private boolean enableIgnoreGpt;
private boolean enableIgnoreOsEvt;
// Variables to get policy settings from page
private String pcValidate;
private String pcAttributeValidate;
private String ecValidate;
private String fmValidate;
private String attestationCertificateIssued;
private String devIdCertificateIssued;
private String generationExpirationOn;
private String devIdExpirationChecked;
private String numOfValidDays;
private String reissueThreshold;
private String devIdReissueThreshold;
private String ignoreIma;
private String ignoretBoot;
private String ignoreGpt;
private String ignoreOsEvt;
private String expirationValue;
private String devIdExpirationValue;
private String thresholdValue;
private String devIdThresholdValue;
/**
* Constructor. Sets fields from policy.
*
* @param policy The supply chain policy
*/
public PolicyPageModel(final SupplyChainSettings policy) {
this.enableEcValidation = policy.isEcValidationEnabled();
this.enablePcCertificateValidation = policy.isPcValidationEnabled();
this.enablePcCertificateAttributeValidation = policy.isPcAttributeValidationEnabled();
this.enableFirmwareValidation = policy.isFirmwareValidationEnabled();
this.issueAttestationCertificate = policy.isIssueAttestationCertificate();
this.issueDevIdCertificate = policy.isIssueDevIdCertificate();
this.generateOnExpiration = policy.isGenerateOnExpiration();
this.devIdExpirationFlag = policy.isDevIdExpirationFlag();
this.numOfValidDays = policy.getValidityDays();
this.reissueThreshold = policy.getReissueThreshold();
this.expirationValue = policy.getValidityDays();
this.thresholdValue = policy.getReissueThreshold();
this.devIdExpirationValue = policy.getDevIdValidityDays();
this.devIdReissueThreshold = policy.getDevIdReissueThreshold();
this.devIdThresholdValue = policy.getDevIdReissueThreshold();
// pcrPolicy
this.enableIgnoreIma = policy.isIgnoreImaEnabled();
this.enableIgnoreTboot = policy.isIgnoretBootEnabled();
this.enableIgnoreGpt = policy.isIgnoreGptEnabled();
this.enableIgnoreOsEvt = policy.isIgnoreOsEvtEnabled();
}
}

View File

@ -1,122 +0,0 @@
package hirs.attestationca.portal.page.controllers;
import hirs.attestationca.portal.page.PageController;
import hirs.attestationca.portal.page.PageMessages;
import hirs.attestationca.portal.page.params.CertificateDetailsPageParams;
import hirs.attestationca.portal.util.CertificateStringMapBuilder;
import hirs.persist.CertificateManager;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import java.io.IOException;
import java.util.HashMap;
import java.util.UUID;
import static hirs.attestationca.portal.page.Page.CERTIFICATE_DETAILS;
/**
* Controller for the Certificate Details page.
*/
@Controller
@RequestMapping("/certificate-details")
public class CertificateDetailsPageController extends PageController<CertificateDetailsPageParams> {
/**
* Model attribute name used by initPage for the initial data passed to the page.
*/
static final String INITIAL_DATA = "initialData";
private final CertificateManager certificateManager;
private static final Logger LOGGER =
LogManager.getLogger(CertificateDetailsPageController.class);
/**
* Constructor providing the Page's display and routing specification.
* @param certificateManager the certificate manager
*/
@Autowired
public CertificateDetailsPageController(final CertificateManager certificateManager) {
super(CERTIFICATE_DETAILS);
this.certificateManager = certificateManager;
}
/**
* Returns the path for the view and the data model for the page.
*
* @param params The object to map url parameters into.
* @param model The data model for the request. Can contain data from redirect.
* @return the path for the view and data model for the page.
*/
@Override
@RequestMapping
public ModelAndView initPage(final CertificateDetailsPageParams params, final Model model) {
// get the basic information to render the page
ModelAndView mav = getBaseModelAndView();
PageMessages messages = new PageMessages();
// Map with the certificate information
HashMap<String, Object> data = new HashMap<>();
// Check if parameters were set
if (params.getId() == null) {
String typeError = "ID was not provided";
messages.addError(typeError);
LOGGER.error(typeError);
mav.addObject(MESSAGES_ATTRIBUTE, messages);
} else if (params.getType() == null) {
String typeError = "Type was not provided";
messages.addError(typeError);
LOGGER.error(typeError);
mav.addObject(MESSAGES_ATTRIBUTE, messages);
} else {
try {
String type = params.getType().toLowerCase();
UUID uuid = UUID.fromString(params.getId());
switch (type) {
case "certificateauthority":
data.putAll(CertificateStringMapBuilder.getCertificateAuthorityInformation(
uuid, certificateManager));
break;
case "endorsement":
data.putAll(CertificateStringMapBuilder.getEndorsementInformation(uuid,
certificateManager));
break;
case "platform":
data.putAll(CertificateStringMapBuilder.getPlatformInformation(uuid,
certificateManager));
break;
case "issued":
data.putAll(CertificateStringMapBuilder.getIssuedInformation(uuid,
certificateManager));
break;
default:
String typeError = "Invalid certificate type: " + params.getType();
messages.addError(typeError);
LOGGER.error(typeError);
mav.addObject(MESSAGES_ATTRIBUTE, messages);
break;
}
} catch (IllegalArgumentException | IOException ex) {
String uuidError = "Failed to parse ID from: " + params.getId();
messages.addError(uuidError);
LOGGER.error(uuidError, ex);
}
if (data.isEmpty()) {
String notFoundMessage = "Unable to find certificate with ID: " + params.getId();
messages.addError(notFoundMessage);
LOGGER.warn(notFoundMessage);
mav.addObject(MESSAGES_ATTRIBUTE, messages);
} else {
mav.addObject(INITIAL_DATA, data);
}
}
// return the model and view
return mav;
}
}

View File

@ -1,955 +0,0 @@
package hirs.attestationca.portal.page.controllers;
import hirs.FilteredRecordsList;
import hirs.attestationca.portal.datatables.DataTableInput;
import hirs.attestationca.portal.datatables.DataTableResponse;
import hirs.attestationca.portal.datatables.OrderedListQueryDataTableAdapter;
import hirs.attestationca.portal.page.Page;
import hirs.attestationca.portal.page.PageController;
import hirs.attestationca.portal.page.PageMessages;
import hirs.attestationca.portal.page.params.NoPageParams;
import hirs.attestationca.portal.util.CertificateStringMapBuilder;
import hirs.data.persist.certificate.Certificate;
import hirs.data.persist.certificate.CertificateAuthorityCredential;
import hirs.data.persist.certificate.EndorsementCredential;
import hirs.data.persist.certificate.IssuedAttestationCertificate;
import hirs.data.persist.certificate.PlatformCredential;
import hirs.persist.CertificateManager;
import hirs.persist.CriteriaModifier;
import hirs.persist.CrudManager;
import hirs.persist.DBManagerException;
import hirs.persist.OrderedListQuerier;
import org.apache.logging.log4j.Logger;
import org.bouncycastle.util.encoders.DecoderException;
import org.hibernate.Criteria;
import org.hibernate.criterion.Restrictions;
import org.hibernate.sql.JoinType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.StreamUtils;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import org.springframework.web.servlet.view.RedirectView;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URISyntaxException;
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import static org.apache.logging.log4j.LogManager.getLogger;
/**
* Controller for the Certificates list all pages.
*/
@Controller
@RequestMapping("/certificate-request")
public class CertificateRequestPageController extends PageController<NoPageParams> {
private final CertificateManager certificateManager;
private final OrderedListQuerier<Certificate> dataTableQuerier;
private CertificateAuthorityCredential certificateAuthorityCredential;
private static final Logger LOGGER = getLogger(CertificateRequestPageController.class);
private static final String TRUSTCHAIN = "trust-chain";
private static final String PLATFORMCREDENTIAL = "platform-credentials";
private static final String ENDORSEMENTCREDENTIAL = "endorsement-key-credentials";
private static final String ISSUEDCERTIFICATES = "issued-certificates";
/**
* Model attribute name used by initPage for the aca cert info.
*/
static final String ACA_CERT_DATA = "acaCertData";
/**
* Constructor providing the Page's display and routing specification.
*
* @param certificateManager the certificate manager
* @param crudManager the CRUD manager for certificates
* @param acaCertificate the ACA's X509 certificate
*/
@Autowired
public CertificateRequestPageController(
final CertificateManager certificateManager,
final CrudManager<Certificate> crudManager,
final X509Certificate acaCertificate) {
super(Page.TRUST_CHAIN);
this.certificateManager = certificateManager;
this.dataTableQuerier = crudManager;
try {
certificateAuthorityCredential
= new CertificateAuthorityCredential(acaCertificate.getEncoded());
} catch (IOException e) {
LOGGER.error("Failed to read ACA certificate", e);
} catch (CertificateEncodingException e) {
LOGGER.error("Error getting encoded ACA certificate", e);
}
}
/**
* Returns the path for the view and the data model for the page.
*
* @param params The object to map url parameters into.
* @param model The data model for the request. Can contain data from
* redirect.
* @return the path for the view and data model for the page.
*/
@Override
@RequestMapping
public ModelAndView initPage(final NoPageParams params, final Model model) {
return getBaseModelAndView();
}
/**
* Returns the path for the view and the data model for the page.
*
* @param certificateType String containing the certificate type
* @param params The object to map url parameters into.
* @param model The data model for the request. Can contain data from
* redirect.
* @return the path for the view and data model for the page.
*/
@RequestMapping("/{certificateType}")
public ModelAndView initPage(@PathVariable("certificateType") final String certificateType,
final NoPageParams params, final Model model) {
ModelAndView mav = null;
HashMap<String, String> data = new HashMap<>();
// add page information
switch (certificateType) {
case PLATFORMCREDENTIAL:
mav = getBaseModelAndView(Page.PLATFORM_CREDENTIALS);
break;
case ENDORSEMENTCREDENTIAL:
mav = getBaseModelAndView(Page.ENDORSEMENT_KEY_CREDENTIALS);
break;
case ISSUEDCERTIFICATES:
mav = getBaseModelAndView(Page.ISSUED_CERTIFICATES);
break;
case TRUSTCHAIN:
mav = getBaseModelAndView(Page.TRUST_CHAIN);
// Map with the ACA certificate information
data.putAll(CertificateStringMapBuilder.getCertificateAuthorityInformation(
certificateAuthorityCredential, this.certificateManager));
mav.addObject(ACA_CERT_DATA, data);
break;
default:
// send to an error page
break;
}
return mav;
}
/**
* Queries for the list of Certificates and returns a data table response
* with the records.
*
* @param certificateType String containing the certificate type
* @param input the DataTables search/query parameters
* @return the data table
*/
@ResponseBody
@RequestMapping(value = "/{certificateType}/list",
produces = MediaType.APPLICATION_JSON_VALUE,
method = RequestMethod.GET)
@SuppressWarnings("unchecked")
public DataTableResponse<? extends Certificate> getTableData(
@PathVariable("certificateType") final String certificateType,
final DataTableInput input) {
LOGGER.debug("Handling list request: " + input);
// attempt to get the column property based on the order index.
String orderColumnName = input.getOrderColumnName();
LOGGER.debug("Ordering on column: " + orderColumnName);
// check that the alert is not archived and that it is in the specified report
CriteriaModifier criteriaModifier = new CriteriaModifier() {
@Override
public void modify(final Criteria criteria) {
criteria.add(Restrictions.isNull(Certificate.ARCHIVE_FIELD));
// add a device alias if this query includes the device table
// for getting the device (e.g. device name).
// use left join, since device may be null. Query will return all
// Certs of this type, whether it has a Device or not (device field may be null)
if (hasDeviceTableToJoin(certificateType)) {
criteria.createAlias("device", "device", JoinType.LEFT_OUTER_JOIN);
}
}
};
FilteredRecordsList records
= OrderedListQueryDataTableAdapter.getOrderedList(
getCertificateClass(certificateType), dataTableQuerier,
input, orderColumnName, criteriaModifier);
// special parsing for platform credential
// Add the EndorsementCredential for each PlatformCredential based on the
// serial number. (pc.HolderSerialNumber = ec.SerialNumber)
if (certificateType.equals(PLATFORMCREDENTIAL)) {
EndorsementCredential associatedEC;
if (!records.isEmpty()) {
// loop all the platform certificates
for (int i = 0; i < records.size(); i++) {
PlatformCredential pc = (PlatformCredential) records.get(i);
// find the EC using the PC's "holder serial number"
associatedEC = EndorsementCredential
.select(certificateManager)
.bySerialNumber(pc.getHolderSerialNumber())
.getCertificate();
if (associatedEC != null) {
LOGGER.debug("EC ID for holder s/n " + pc
.getHolderSerialNumber() + " = " + associatedEC.getId());
}
pc.setEndorsementCredential(associatedEC);
}
}
}
LOGGER.debug("Returning list of size: " + records.size());
return new DataTableResponse<>(records, input);
}
/**
* Archives (soft delete) the credential.
*
* @param certificateType String containing the certificate type
* @param id the UUID of the cert to delete
* @param attr RedirectAttributes used to forward data back to the original
* page.
* @return redirect to this page
* @throws URISyntaxException if malformed URI
*/
@RequestMapping(value = "/{certificateType}/delete", method = RequestMethod.POST)
public RedirectView delete(
@PathVariable("certificateType") final String certificateType,
@RequestParam final String id,
final RedirectAttributes attr) throws URISyntaxException {
LOGGER.info("Handling request to delete " + id);
Map<String, Object> model = new HashMap<>();
PageMessages messages = new PageMessages();
try {
UUID uuid = UUID.fromString(id);
Certificate certificate = getCertificateById(certificateType, uuid, certificateManager);
if (certificate == null) {
// Use the term "record" here to avoid user confusion b/t cert and cred
String notFoundMessage = "Unable to locate record with ID: " + uuid;
messages.addError(notFoundMessage);
LOGGER.warn(notFoundMessage);
} else {
if (certificateType.equals(PLATFORMCREDENTIAL)) {
PlatformCredential platformCertificate = (PlatformCredential) certificate;
if (platformCertificate.isBase()) {
// only do this if the base is being deleted.
List<PlatformCredential> sharedCertificates = getCertificateByBoardSN(
certificateType,
platformCertificate.getPlatformSerial(),
certificateManager);
if (sharedCertificates != null) {
for (PlatformCredential pc : sharedCertificates) {
if (!pc.isBase()) {
pc.archive();
certificateManager.update(pc);
}
}
}
}
}
certificate.archive();
certificateManager.update(certificate);
String deleteCompletedMessage = "Certificate successfully deleted";
messages.addInfo(deleteCompletedMessage);
LOGGER.info(deleteCompletedMessage);
}
} catch (IllegalArgumentException ex) {
String uuidError = "Failed to parse ID from: " + id;
messages.addError(uuidError);
LOGGER.error(uuidError, ex);
} catch (DBManagerException ex) {
String dbError = "Failed to archive cert: " + id;
messages.addError(dbError);
LOGGER.error(dbError, ex);
}
model.put(MESSAGES_ATTRIBUTE, messages);
return redirectTo(getCertificatePage(certificateType), new NoPageParams(), model, attr);
}
/**
* Handles request to download the cert by writing it to the response stream
* for download.
*
* @param certificateType String containing the certificate type
* @param id the UUID of the cert to download
* @param response the response object (needed to update the header with the
* file name)
* @throws java.io.IOException when writing to response output stream
*/
@RequestMapping(value = "/{certificateType}/download", method = RequestMethod.GET)
public void download(
@PathVariable("certificateType") final String certificateType,
@RequestParam final String id,
final HttpServletResponse response)
throws IOException {
LOGGER.info("Handling request to download " + id);
try {
UUID uuid = UUID.fromString(id);
Certificate certificate = getCertificateById(certificateType, uuid, certificateManager);
if (certificate == null) {
// Use the term "record" here to avoid user confusion b/t cert and cred
String notFoundMessage = "Unable to locate record with ID: " + uuid;
LOGGER.warn(notFoundMessage);
// send a 404 error when invalid certificate
response.sendError(HttpServletResponse.SC_NOT_FOUND);
} else {
StringBuilder fileName = new StringBuilder("filename=\"");
fileName.append(getCertificateClass(certificateType).getSimpleName());
fileName.append("_");
fileName.append(certificate.getSerialNumber());
fileName.append(".cer\"");
// Set filename for download.
response.setHeader("Content-Disposition", "attachment;" + fileName);
response.setContentType("application/octet-stream");
// write cert to output stream
response.getOutputStream().write(certificate.getRawBytes());
}
} catch (IllegalArgumentException ex) {
String uuidError = "Failed to parse ID from: " + id;
LOGGER.error(uuidError, ex);
// send a 404 error when invalid certificate
response.sendError(HttpServletResponse.SC_NOT_FOUND);
}
}
/**
* Handles request to download the ACA cert by writing it to the response
* stream for download.
*
* @param response the response object (needed to update the header with the
* file name)
*
* @throws java.io.IOException when writing to response output stream
*/
@ResponseBody
@RequestMapping(value = "/trust-chain/download-aca-cert", method = RequestMethod.GET)
public void downloadAcaCertificate(final HttpServletResponse response)
throws IOException {
// Set filename for download.
response.setHeader("Content-Disposition", "attachment; filename=\"hirs-aca-cert.cer\"");
response.setContentType("application/octet-stream");
// write cert to output stream
response.getOutputStream().write(certificateAuthorityCredential.getRawBytes());
}
/**
* Handles request to download the certs by writing it to the response stream
* for download in bulk.
*
* @param response the response object (needed to update the header with the
* file name)
* @throws java.io.IOException when writing to response output stream
*/
@RequestMapping(value = "/trust-chain/bulk", method = RequestMethod.GET)
public void caBulkDownload(final HttpServletResponse response)
throws IOException {
LOGGER.info("Handling request to download all trust chain certificates");
String fileName = "trust-chain.zip";
String zipFileName;
// Set filename for download.
response.setHeader("Content-Disposition", "attachment; filename=" + fileName);
response.setContentType("application/zip");
try (ZipOutputStream zipOut = new ZipOutputStream(response.getOutputStream())) {
// get all files
for (CertificateAuthorityCredential ca : CertificateAuthorityCredential
.select(certificateManager)
.getCertificates()) {
zipFileName = String.format("ca-certificates[%s].cer",
Integer.toHexString(ca.getCertificateHash()));
// configure the zip entry, the properties of the 'file'
ZipEntry zipEntry = new ZipEntry(zipFileName);
zipEntry.setSize((long) ca.getRawBytes().length * Byte.SIZE);
zipEntry.setTime(System.currentTimeMillis());
zipOut.putNextEntry(zipEntry);
// the content of the resource
StreamUtils.copy(ca.getRawBytes(), zipOut);
zipOut.closeEntry();
}
zipOut.finish();
// write cert to output stream
} catch (IllegalArgumentException ex) {
String uuidError = "Failed to parse ID from: ";
LOGGER.error(uuidError, ex);
// send a 404 error when invalid certificate
response.sendError(HttpServletResponse.SC_NOT_FOUND);
}
}
/**
* Handles request to download the certs by writing it to the response stream
* for download in bulk.
*
* @param response the response object (needed to update the header with the
* file name)
* @throws java.io.IOException when writing to response output stream
*/
@RequestMapping(value = "/platform-credentials/bulk", method = RequestMethod.GET)
public void pcBulkDownload(final HttpServletResponse response)
throws IOException {
LOGGER.info("Handling request to download all platform certificates");
String fileName = "platform_certificates.zip";
String zipFileName;
// Set filename for download.
response.setHeader("Content-Disposition", "attachment; filename=" + fileName);
response.setContentType("application/zip");
try (ZipOutputStream zipOut = new ZipOutputStream(response.getOutputStream())) {
// get all files
for (PlatformCredential pc : PlatformCredential.select(certificateManager)
.getCertificates()) {
zipFileName = String.format("Platform_Certificates[%s].cer",
Integer.toHexString(pc.getCertificateHash()));
// configure the zip entry, the properties of the 'file'
ZipEntry zipEntry = new ZipEntry(zipFileName);
zipEntry.setSize((long) pc.getRawBytes().length * Byte.SIZE);
zipEntry.setTime(System.currentTimeMillis());
zipOut.putNextEntry(zipEntry);
// the content of the resource
StreamUtils.copy(pc.getRawBytes(), zipOut);
zipOut.closeEntry();
}
zipOut.finish();
// write cert to output stream
} catch (IllegalArgumentException ex) {
String uuidError = "Failed to parse ID from: ";
LOGGER.error(uuidError, ex);
// send a 404 error when invalid certificate
response.sendError(HttpServletResponse.SC_NOT_FOUND);
}
}
/**
* Handles request to download the certs by writing it to the response stream
* for download in bulk.
*
* @param response the response object (needed to update the header with the
* file name)
* @throws java.io.IOException when writing to response output stream
*/
@RequestMapping(value = "/issued-certificates/bulk", method = RequestMethod.GET)
public void icBulkDownload(final HttpServletResponse response)
throws IOException {
LOGGER.info("Handling request to download all issued certificates");
String fileName = "issued_certificates.zip";
String zipFileName;
// Set filename for download.
response.setHeader("Content-Disposition", "attachment; filename=" + fileName);
response.setContentType("application/zip");
try (ZipOutputStream zipOut = new ZipOutputStream(response.getOutputStream())) {
// get all files
for (IssuedAttestationCertificate ic : IssuedAttestationCertificate
.select(certificateManager)
.getCertificates()) {
zipFileName = String.format("Issued_Certificates[%s].cer",
Integer.toHexString(ic.getCertificateHash()));
// configure the zip entry, the properties of the 'file'
ZipEntry zipEntry = new ZipEntry(zipFileName);
zipEntry.setSize((long) ic.getRawBytes().length * Byte.SIZE);
zipEntry.setTime(System.currentTimeMillis());
zipOut.putNextEntry(zipEntry);
// the content of the resource
StreamUtils.copy(ic.getRawBytes(), zipOut);
zipOut.closeEntry();
}
zipOut.finish();
// write cert to output stream
} catch (IllegalArgumentException ex) {
String uuidError = "Failed to parse ID from: ";
LOGGER.error(uuidError, ex);
// send a 404 error when invalid certificate
response.sendError(HttpServletResponse.SC_NOT_FOUND);
}
}
/**
* Handles request to download the certs by writing it to the response stream
* for download in bulk.
*
* @param response the response object (needed to update the header with the
* file name)
* @throws java.io.IOException when writing to response output stream
*/
@RequestMapping(value = "/endorsement-key-credentials/bulk", method = RequestMethod.GET)
public void ekBulkDownload(final HttpServletResponse response)
throws IOException {
LOGGER.info("Handling request to download all endorsement certificates");
String fileName = "endorsement_certificates.zip";
String zipFileName;
// Set filename for download.
response.setHeader("Content-Disposition", "attachment; filename=" + fileName);
response.setContentType("application/zip");
try (ZipOutputStream zipOut = new ZipOutputStream(response.getOutputStream())) {
// get all files
for (EndorsementCredential ek : EndorsementCredential
.select(certificateManager)
.getCertificates()) {
zipFileName = String.format("Endorsement_Certificates[%s].cer",
Integer.toHexString(ek.getCertificateHash()));
// configure the zip entry, the properties of the 'file'
ZipEntry zipEntry = new ZipEntry(zipFileName);
zipEntry.setSize((long) ek.getRawBytes().length * Byte.SIZE);
zipEntry.setTime(System.currentTimeMillis());
zipOut.putNextEntry(zipEntry);
// the content of the resource
StreamUtils.copy(ek.getRawBytes(), zipOut);
zipOut.closeEntry();
}
zipOut.finish();
// write cert to output stream
} catch (IllegalArgumentException ex) {
String uuidError = "Failed to parse ID from: ";
LOGGER.error(uuidError, ex);
// send a 404 error when invalid certificate
response.sendError(HttpServletResponse.SC_NOT_FOUND);
}
}
/**
* Upload and processes a credential.
*
* @param certificateType String containing the certificate type
* @param files the files to process
* @param attr the redirection attributes
* @return the redirection view
* @throws URISyntaxException if malformed URI
*/
@RequestMapping(value = "/{certificateType}/upload", method = RequestMethod.POST)
protected RedirectView upload(
@PathVariable("certificateType") final String certificateType,
@RequestParam("file") final MultipartFile[] files,
final RedirectAttributes attr) throws URISyntaxException {
Map<String, Object> model = new HashMap<>();
PageMessages messages = new PageMessages();
for (MultipartFile file : files) {
//Parse certificate
Certificate certificate = parseCertificate(certificateType, file, messages);
//Store only if it was parsed
if (certificate != null) {
storeCertificate(
certificateType,
file.getOriginalFilename(),
messages, certificate,
certificateManager);
}
}
//Add messages to the model
model.put(MESSAGES_ATTRIBUTE, messages);
return redirectTo(getCertificatePage(certificateType), new NoPageParams(), model, attr);
}
/**
* Get the page based on the certificate type.
*
* @param certificateType String containing the certificate type
* @return the page for the certificate type.
*/
private static Page getCertificatePage(final String certificateType) {
// get page information (default to TRUST_CHAIN)
switch (certificateType) {
case PLATFORMCREDENTIAL:
return Page.PLATFORM_CREDENTIALS;
case ENDORSEMENTCREDENTIAL:
return Page.ENDORSEMENT_KEY_CREDENTIALS;
case ISSUEDCERTIFICATES:
return Page.ISSUED_CERTIFICATES;
case TRUSTCHAIN:
default:
return Page.TRUST_CHAIN;
}
}
/**
* Gets the concrete certificate class type to query for.
*
* @param certificateType String containing the certificate type
* @return the certificate class type
*/
private static Class<? extends Certificate> getCertificateClass(final String certificateType) {
switch (certificateType) {
case PLATFORMCREDENTIAL:
return PlatformCredential.class;
case ENDORSEMENTCREDENTIAL:
return EndorsementCredential.class;
case ISSUEDCERTIFICATES:
return IssuedAttestationCertificate.class;
case TRUSTCHAIN:
return CertificateAuthorityCredential.class;
default:
throw new IllegalArgumentException(
String.format("Unknown certificate type: %s", certificateType));
}
}
/**
* Get flag indicating if a device-name join/alias is required for
* displaying the table data. This will be true if displaying a cert that is
* associated with a device.
*
* @param certificateType String containing the certificate type
* @return true if the list criteria modifier requires aliasing the device
* table, false otherwise.
*/
private boolean hasDeviceTableToJoin(final String certificateType) {
boolean hasDevice = true;
// Trust_Chain Credential do not contain the device table to join.
if (certificateType.equals(TRUSTCHAIN)) {
hasDevice = false;
}
return hasDevice;
}
/**
* Gets the certificate by ID.
*
* @param certificateType String containing the certificate type
* @param uuid the ID of the cert
* @param certificateManager the certificate manager to query
* @return the certificate or null if none is found
*/
private Certificate getCertificateById(
final String certificateType,
final UUID uuid,
final CertificateManager certificateManager) {
switch (certificateType) {
case PLATFORMCREDENTIAL:
return PlatformCredential
.select(certificateManager)
.byEntityId(uuid)
.getCertificate();
case ENDORSEMENTCREDENTIAL:
return EndorsementCredential
.select(certificateManager)
.byEntityId(uuid)
.getCertificate();
case ISSUEDCERTIFICATES:
return IssuedAttestationCertificate
.select(certificateManager)
.byEntityId(uuid)
.getCertificate();
case TRUSTCHAIN:
return CertificateAuthorityCredential
.select(certificateManager)
.byEntityId(uuid)
.getCertificate();
default:
return null;
}
}
/**
* Gets the certificate by the hash code of its bytes. Looks for both
* archived and unarchived certificates.
*
* @param certificateType String containing the certificate type
* @param certificateHash the hash of the certificate's bytes
* @param certificateManager the certificate manager to query
* @return the certificate or null if none is found
*/
private Certificate getCertificateByHash(
final String certificateType,
final int certificateHash,
final CertificateManager certificateManager) {
switch (certificateType) {
case PLATFORMCREDENTIAL:
return PlatformCredential
.select(certificateManager)
.includeArchived()
.byHashCode(certificateHash)
.getCertificate();
case ENDORSEMENTCREDENTIAL:
return EndorsementCredential
.select(certificateManager)
.includeArchived()
.byHashCode(certificateHash)
.getCertificate();
case TRUSTCHAIN:
return CertificateAuthorityCredential
.select(certificateManager)
.includeArchived()
.byHashCode(certificateHash)
.getCertificate();
default:
return null;
}
}
/**
* Gets the certificate by the platform serial number.
*
* @param certificateType String containing the certificate type
* @param serialNumber the platform serial number
* @param certificateManager the certificate manager to query
* @return the certificate or null if none is found
*/
private List<PlatformCredential> getCertificateByBoardSN(
final String certificateType,
final String serialNumber,
final CertificateManager certificateManager) {
if (serialNumber == null) {
return null;
}
switch (certificateType) {
case PLATFORMCREDENTIAL:
return PlatformCredential
.select(certificateManager)
.byBoardSerialNumber(serialNumber)
.getCertificates().stream().collect(Collectors.toList());
default:
return null;
}
}
/**
* Parses an uploaded file into a certificate and populates the given model
* with error messages if parsing fails.
*
* @param certificateType String containing the certificate type
* @param file the file being uploaded from the portal
* @param messages contains any messages that will be display on the page
* @return the parsed certificate or null if parsing failed.
*/
private Certificate parseCertificate(
final String certificateType,
final MultipartFile file,
final PageMessages messages) {
LOGGER.info("Received File of Size: " + file.getSize());
byte[] fileBytes;
String fileName = file.getOriginalFilename();
// build the certificate from the uploaded bytes
try {
fileBytes = file.getBytes();
} catch (IOException e) {
final String failMessage = String.format(
"Failed to read uploaded file (%s): ", fileName);
LOGGER.error(failMessage, e);
messages.addError(failMessage + e.getMessage());
return null;
}
try {
switch (certificateType) {
case PLATFORMCREDENTIAL:
return new PlatformCredential(fileBytes);
case ENDORSEMENTCREDENTIAL:
return new EndorsementCredential(fileBytes);
case TRUSTCHAIN:
return new CertificateAuthorityCredential(fileBytes);
default:
final String failMessage = String.format("Failed to parse uploaded file "
+ "(%s). Invalid certificate type: %s", fileName, certificateType);
LOGGER.error(failMessage);
messages.addError(failMessage);
return null;
}
} catch (IOException e) {
final String failMessage = String.format(
"Failed to parse uploaded file (%s): ", fileName);
LOGGER.error(failMessage, e);
messages.addError(failMessage + e.getMessage());
return null;
} catch (DecoderException dEx) {
final String failMessage = String.format(
"Failed to parse uploaded pem file (%s): ", fileName);
LOGGER.error(failMessage, dEx);
messages.addError(failMessage + dEx.getMessage());
return null;
} catch (IllegalArgumentException e) {
final String failMessage = String.format(
"Certificate format not recognized(%s): ", fileName);
LOGGER.error(failMessage, e);
messages.addError(failMessage + e.getMessage());
return null;
}
}
/**
* Store the given certificate in the database.
*
* @param certificateType String containing the certificate type
* @param fileName contain the name of the file of the certificate to
* be stored
* @param messages contains any messages that will be display on the page
* @param certificate the certificate to store
* @param certificateManager the DB manager to use
* @return the messages for the page
*/
private void storeCertificate(
final String certificateType,
final String fileName,
final PageMessages messages,
final Certificate certificate,
final CertificateManager certificateManager) {
Certificate existingCertificate;
// look for an identical certificate in the database
try {
existingCertificate = getCertificateByHash(
certificateType,
certificate.getCertificateHash(),
certificateManager);
} catch (DBManagerException e) {
final String failMessage = "Querying for existing certificate failed ("
+ fileName + "): ";
messages.addError(failMessage + e.getMessage());
LOGGER.error(failMessage, e);
return;
}
try {
// save the new certificate if no match is found
if (existingCertificate == null) {
if (certificateType.equals(PLATFORMCREDENTIAL)) {
PlatformCredential platformCertificate = (PlatformCredential) certificate;
if (platformCertificate.isBase()) {
List<PlatformCredential> sharedCertificates = getCertificateByBoardSN(
certificateType,
platformCertificate.getPlatformSerial(),
certificateManager);
if (sharedCertificates != null) {
for (PlatformCredential pc : sharedCertificates) {
if (pc.isBase()) {
final String failMessage = "Storing certificate failed: "
+ "platform credential "
+ "chain (" + pc.getPlatformSerial()
+ ") base already exists in this chain ("
+ fileName + ")";
messages.addError(failMessage);
LOGGER.error(failMessage);
return;
}
}
}
} /**else {
// this is a delta, check if the holder exists.
PlatformCredential holderPC = PlatformCredential
.select(certificateManager)
.bySerialNumber(platformCertificate.getHolderSerialNumber())
.getCertificate();
if (holderPC == null) {
final String failMessage = "Storing certificate failed: "
+ "delta credential"
+ " must have an existing holder stored. "
+ "Credential serial "
+ platformCertificate.getHolderSerialNumber()
+ " doesn't exist.";
messages.addError(failMessage);
LOGGER.error(failMessage);
return;
}
}**/
}
certificateManager.save(certificate);
final String successMsg
= String.format("New certificate successfully uploaded (%s): ", fileName);
messages.addSuccess(successMsg);
LOGGER.info(successMsg);
return;
}
} catch (DBManagerException e) {
final String failMessage = String.format("Storing new certificate failed (%s): ",
fileName);
messages.addError(failMessage + e.getMessage());
LOGGER.error(failMessage, e);
return;
}
try {
// if an identical certificate is archived, update the existing certificate to
// unarchive it and change the creation date
if (existingCertificate.isArchived()) {
existingCertificate.restore();
existingCertificate.resetCreateTime();
certificateManager.update(existingCertificate);
final String successMsg = String.format("Pre-existing certificate "
+ "found and unarchived (%s): ", fileName);
messages.addSuccess(successMsg);
LOGGER.info(successMsg);
return;
}
} catch (DBManagerException e) {
final String failMessage = String.format("Found an identical"
+ " pre-existing certificate in the "
+ "archive, but failed to unarchive it (%s): ", fileName);
messages.addError(failMessage + e.getMessage());
LOGGER.error(failMessage, e);
return;
}
// if an identical certificate is already unarchived, do nothing and show a fail message
final String failMessage
= String.format("Storing certificate failed: an identical"
+ " certificate already exists (%s): ", fileName);
messages.addError(failMessage);
LOGGER.error(failMessage);
}
}

View File

@ -0,0 +1,85 @@
package hirs.attestationca.portal.page.controllers;
import hirs.attestationca.portal.entity.manager.DeviceRepository;
import hirs.attestationca.portal.entity.userdefined.Device;
import hirs.attestationca.portal.enums.AppraisalStatus;
import hirs.attestationca.portal.enums.HealthStatus;
import hirs.attestationca.portal.enums.Page;
import hirs.attestationca.portal.page.PageController;
import hirs.attestationca.portal.page.params.NoPageParams;
import hirs.attestationca.portal.service.DeviceServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
import java.sql.Timestamp;
import java.time.LocalDateTime;
@Controller
@RequestMapping("/devices")
public class DevicePageController extends PageController<NoPageParams> {
/**
* https://odrotbohm.de/2013/11/why-field-injection-is-evil/
*
* Autowiring property vs constructor
*/
private final DeviceServiceImpl deviceServiceImpl;
private final DeviceRepository deviceRepository;
@Autowired
public DevicePageController(DeviceServiceImpl deviceServiceImpl,
DeviceRepository deviceRepository) {
super(Page.DEVICES);
this.deviceServiceImpl = deviceServiceImpl;
this.deviceRepository = deviceRepository;
}
@Override
@RequestMapping
public ModelAndView initPage(final NoPageParams params, final Model model) {
return getBaseModelAndView();
}
// @RequestMapping(value = "list", produces = MediaType.APPLICATION_JSON_VALUE,
// method = RequestMethod.GET)
// public DataTableResponse<HashMap<String, Object>> getTableData(
// final DataTableInput input) {
// String orderColumnName = input.getOrderColumnName();
// FilteredRecordsList<HashMap<String, Object>> record
// = retrieveDevicesAndAssociatedCertificates(deviceList);
// modelMap.put("devices", deviceServiceImpl.retrieveDevices());
// return new DataTableResponse<>(record, input);
// }
@GetMapping(value = "populateDevices")
public @ResponseBody String addDevice () {
deviceRepository.save(new Device("Dell-01", HealthStatus.TRUSTED,
AppraisalStatus.Status.UNKNOWN,
Timestamp.valueOf(LocalDateTime.now()), false, "", "This is a summary"));
deviceRepository.save(new Device("Dell-02", HealthStatus.TRUSTED,
AppraisalStatus.Status.UNKNOWN,
Timestamp.valueOf(LocalDateTime.now()), false, "", "This is a summary"));
deviceRepository.save(new Device("HP-01", HealthStatus.UNKNOWN,
AppraisalStatus.Status.UNKNOWN,
Timestamp.valueOf(LocalDateTime.now()), false, "", "This is a summary"));
deviceRepository.save(new Device("HP-02", HealthStatus.UNTRUSTED,
AppraisalStatus.Status.UNKNOWN,
Timestamp.valueOf(LocalDateTime.now()), false, "", "This is a summary"));
return "all";
}
@GetMapping(path="/all")
public @ResponseBody Iterable<Device> getAllDevices() {
return deviceRepository.findAll();
}
}

View File

@ -1,173 +0,0 @@
package hirs.attestationca.portal.page.controllers;
import hirs.attestationca.portal.datatables.DataTableInput;
import hirs.attestationca.portal.datatables.DataTableResponse;
import hirs.attestationca.portal.datatables.OrderedListQueryDataTableAdapter;
import static hirs.attestationca.portal.page.Page.DEVICES;
import hirs.attestationca.portal.page.PageController;
import hirs.attestationca.portal.page.params.NoPageParams;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.UUID;
import static org.apache.logging.log4j.LogManager.getLogger;
import org.hibernate.criterion.Restrictions;
import hirs.FilteredRecordsList;
import hirs.data.persist.Device;
import hirs.data.persist.certificate.Certificate;
import hirs.data.persist.certificate.DeviceAssociatedCertificate;
import hirs.persist.DBManager;
import hirs.persist.DeviceManager;
/**
* Controller for the Device page.
*/
@Controller
@RequestMapping("/devices")
public class DevicesPageController extends PageController<NoPageParams> {
private final DeviceManager deviceManager;
private final DBManager<Certificate> certificateDBManager;
private static final Logger LOGGER = getLogger(DevicesPageController.class);
/**
* Constructor providing the Page's display and routing specification.
* @param deviceManager the device manager
* @param certificateDBManager the certificate DB manager
*/
@Autowired
public DevicesPageController(
final DeviceManager deviceManager,
final DBManager<Certificate> certificateDBManager) {
super(DEVICES);
this.deviceManager = deviceManager;
this.certificateDBManager = certificateDBManager;
}
/**
* Returns the path for the view and the data model for the page.
*
* @param params The object to map url parameters into.
* @param model The data model for the request. Can contain data from redirect.
* @return the path for the view and data model for the page.
*/
@Override
@RequestMapping
public ModelAndView initPage(final NoPageParams params, final Model model) {
return getBaseModelAndView();
}
/**
* Returns the list of devices using the datatable input for paging, ordering, and
* filtering.
* @param input the data tables input
* @return the data tables response, including the result set and paging information
*/
@ResponseBody
@RequestMapping(value = "list", produces = MediaType.APPLICATION_JSON_VALUE,
method = RequestMethod.GET)
public DataTableResponse<HashMap<String, Object>> getTableData(
final DataTableInput input) {
LOGGER.debug("Handling request for device list");
String orderColumnName = input.getOrderColumnName();
// get all the devices
FilteredRecordsList<Device> deviceList =
OrderedListQueryDataTableAdapter.getOrderedList(Device.class,
deviceManager, input, orderColumnName);
FilteredRecordsList<HashMap<String, Object>> record
= retrieveDevicesAndAssociatedCertificates(deviceList);
return new DataTableResponse<>(record, input);
}
/**
* Returns the list of devices combined with the certificates.
* @param deviceList list containing the devices
* @return a record list after the device and certificate was mapped together.
*/
private FilteredRecordsList<HashMap<String, Object>> retrieveDevicesAndAssociatedCertificates(
final FilteredRecordsList<Device> deviceList) {
FilteredRecordsList<HashMap<String, Object>> records = new FilteredRecordsList<>();
// hashmap containing the device-certificate relationship
HashMap<String, Object> deviceCertMap = new HashMap<>();
Device device;
Certificate certificate;
// parse if there is a Device
if (!deviceList.isEmpty()) {
// get a list of Certificates that contains the device IDs from the list
List<Certificate> certificateList = certificateDBManager.getList(
Certificate.class,
Restrictions.in("device.id", getDevicesIds(deviceList).toArray()));
// loop all the devices
for (int i = 0; i < deviceList.size(); i++) {
// hashmap containing the list of certificates based on the certificate type
HashMap<String, List<Object>> certificatePropertyMap = new HashMap<>();
device = deviceList.get(i);
deviceCertMap.put("device", device);
// loop all the certificates and combined the ones that match the ID
for (int j = 0; j < certificateList.size(); j++) {
certificate = certificateList.get(j);
// set the certificate if it's the same ID
if (device.getId().equals(
((DeviceAssociatedCertificate) certificate).getDevice().getId())) {
String certificateId = certificate.getClass().getSimpleName();
// create a new list for the certificate type if does not exist
// else add it to the current certificate type list
List<Object> certificateListFromMap
= certificatePropertyMap.get(certificateId);
if (certificateListFromMap != null) {
certificateListFromMap.add(certificate);
} else {
certificatePropertyMap.put(certificateId,
new ArrayList<>(Collections.singletonList(certificate)));
}
}
}
// add the device-certificate map to the record
deviceCertMap.putAll(certificatePropertyMap);
records.add(new HashMap<>(deviceCertMap));
deviceCertMap.clear();
}
}
// set pagination values
records.setRecordsTotal(deviceList.getRecordsTotal());
records.setRecordsFiltered(deviceList.getRecordsFiltered());
return records;
}
/**
* Returns the list of devices IDs.
* @param deviceList list containing the devices
* @return a list of the devices IDs
*/
private List<UUID> getDevicesIds(final FilteredRecordsList<Device> deviceList) {
List<UUID> deviceIds = new ArrayList<UUID>();
// loop all the devices
for (int i = 0; i < deviceList.size(); i++) {
deviceIds.add(deviceList.get(i).getId());
}
return deviceIds;
}
}

View File

@ -0,0 +1,23 @@
package hirs.attestationca.portal.page.controllers;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.ModelAndView;
@Controller("error")
public class ErrorController {
@ExceptionHandler(Exception.class)
public ModelAndView handleException(HttpServletRequest request, Exception ex) {
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("exception", ex.getLocalizedMessage());
modelAndView.addObject("url", request.getRequestURL());
modelAndView.setViewName("error");
return modelAndView;
}
}

View File

@ -1,61 +0,0 @@
package hirs.attestationca.portal.page.controllers;
import static hirs.attestationca.portal.page.Page.HELP;
import hirs.attestationca.portal.page.PageController;
import hirs.attestationca.portal.page.params.NoPageParams;
import java.io.File;
import java.io.IOException;
import static org.apache.logging.log4j.LogManager.getLogger;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
/**
* Controller for the Help page.
*/
@Controller
@RequestMapping("/help")
public class HelpController extends PageController<NoPageParams> {
@Autowired
private ApplicationContext applicationContext;
private static final String PATH = "/docs";
private static final Logger LOGGER = getLogger(HelpController.class);
/**
* Constructor providing the Page's display and routing specification.
*/
public HelpController() {
super(HELP);
}
/**
* Returns the path for the view and the data model for the page.
*
* @param params The object to map url parameters into.
* @param model The data model for the request. Can contain data from redirect.
* @return the path for the view and data model for the page.
*/
@Override
@RequestMapping
public ModelAndView initPage(final NoPageParams params, final Model model) {
ModelAndView mav = getBaseModelAndView();
try {
File[] documents = new File(
applicationContext.getResource(PATH).getFile().getPath()
).listFiles();
mav.addObject("docs", documents);
} catch (IOException ex) {
LOGGER.error("Could not get files from resource.");
}
return mav;
}
}

View File

@ -1,16 +1,13 @@
package hirs.attestationca.portal.page.controllers;
import hirs.attestationca.portal.enums.Page;
import hirs.attestationca.portal.page.PageController;
import static hirs.attestationca.portal.page.Page.INDEX;
import hirs.attestationca.portal.page.params.NoPageParams;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
/**
* Controller for the Index page.
*/
@Controller
@RequestMapping("/index")
public class IndexPageController extends PageController<NoPageParams> {
@ -19,7 +16,7 @@ public class IndexPageController extends PageController<NoPageParams> {
* Constructor providing the Page's display and routing specification.
*/
public IndexPageController() {
super(INDEX);
super(Page.INDEX);
}
/**
@ -35,4 +32,9 @@ public class IndexPageController extends PageController<NoPageParams> {
return getBaseModelAndView();
}
// @RequestMapping(value = "/", method = RequestMethod.GET)
// public String showIndexPage(ModelMap model) {
// model.put("name", "welcome");
// return "welcome";
// }
}

View File

@ -1,19 +1,19 @@
package hirs.attestationca.portal.page.controllers;
import hirs.appraiser.Appraiser;
import hirs.appraiser.SupplyChainAppraiser;
import hirs.attestationca.portal.model.PolicyPageModel;
import hirs.attestationca.portal.entity.userdefined.SupplyChainSettings;
import hirs.attestationca.portal.enums.Page;
import hirs.attestationca.portal.page.PageController;
import hirs.attestationca.portal.page.PageMessages;
import hirs.attestationca.portal.page.PolicyPageModel;
import hirs.attestationca.portal.page.params.NoPageParams;
import hirs.data.persist.SupplyChainPolicy;
import hirs.persist.AppraiserManager;
import hirs.persist.PolicyManager;
import hirs.persist.PolicyManagerException;
import hirs.attestationca.portal.service.SettingsServiceImpl;
import hirs.attestationca.portal.utils.exception.PolicyManagerException;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@ -25,9 +25,6 @@ import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.Map;
import static hirs.attestationca.portal.page.Page.POLICY;
import static org.apache.logging.log4j.LogManager.getLogger;
/**
* Controller for the Policy page.
*/
@ -35,7 +32,7 @@ import static org.apache.logging.log4j.LogManager.getLogger;
@RequestMapping("/policy")
public class PolicyPageController extends PageController<NoPageParams> {
private static final Logger LOGGER = getLogger(PolicyPageController.class);
private static final Logger LOGGER = LogManager.getLogger(PolicyPageController.class);
/**
* Represents a web request indicating to enable a setting (based on radio
@ -45,8 +42,7 @@ public class PolicyPageController extends PageController<NoPageParams> {
private static final String ENABLED_EXPIRES_PARAMETER_VALUE = "expires";
private PolicyManager policyManager;
private AppraiserManager appraiserManager;
private SettingsServiceImpl settingsService;
/**
* Model attribute name used by initPage for the initial data passed to the
@ -63,15 +59,16 @@ public class PolicyPageController extends PageController<NoPageParams> {
/**
* Constructor.
*
* @param policyManager the policy manager
* @param appraiserManager the appraiser manager
* @param policyService the policy service
*/
@Autowired
public PolicyPageController(final PolicyManager policyManager,
final AppraiserManager appraiserManager) {
super(POLICY);
this.policyManager = policyManager;
this.appraiserManager = appraiserManager;
public PolicyPageController(final SettingsServiceImpl policyService) {
super(Page.POLICY);
this.settingsService = policyService;
if (this.settingsService.getByName("Default") == null) {
this.settingsService.saveSettings(new SupplyChainSettings("Default", "Settings are configured for no validation flags set."));
}
}
/**
@ -85,11 +82,10 @@ public class PolicyPageController extends PageController<NoPageParams> {
@Override
@RequestMapping
public ModelAndView initPage(final NoPageParams params, final Model model) {
// get the basic information to render the page
ModelAndView mav = getBaseModelAndView();
SupplyChainPolicy policy = getDefaultPolicy();
SupplyChainSettings policy = getDefaultPolicy();
PolicyPageModel pageModel = new PolicyPageModel(policy);
mav.addObject(INITIAL_DATA, pageModel);
@ -106,8 +102,9 @@ public class PolicyPageController extends PageController<NoPageParams> {
* @param attr RedirectAttributes used to forward data back to the original
* page.
* @return View containing the url and parameters
* @throws URISyntaxException if malformed URI
* @throws java.net.URISyntaxException if malformed URI
*/
@GetMapping
@RequestMapping(value = "update-pc-validation", method = RequestMethod.POST)
public RedirectView updatePcVal(@ModelAttribute final PolicyPageModel ppModel,
final RedirectAttributes attr) throws URISyntaxException {
@ -119,7 +116,7 @@ public class PolicyPageController extends PageController<NoPageParams> {
= ppModel.getPcValidate().equalsIgnoreCase(ENABLED_CHECKED_PARAMETER_VALUE);
try {
SupplyChainPolicy policy = getDefaultPolicyAndSetInModel(ppModel, model);
SupplyChainSettings policy = getDefaultPolicyAndSetInModel(ppModel, model);
// If PC policy setting change results in invalid policy, inform user
if (!isPolicyValid(policy.isEcValidationEnabled(), pcValidationOptionEnabled,
@ -140,11 +137,11 @@ public class PolicyPageController extends PageController<NoPageParams> {
}
savePolicyAndApplySuccessMessage(ppModel, model, messages, successMessage, policy);
} catch (PolicyManagerException e) {
} catch (PolicyManagerException pmEx) {
// Log and return any error messages to the user
handlePolicyManagerUpdateError(model, messages, e,
handlePolicyManagerUpdateError(model, messages, pmEx,
"Error changing ACA platform validation Policy",
"Error updating policy. \n" + e.getMessage());
"Error updating policy. \n" + pmEx.getMessage());
}
return redirectToSelf(new NoPageParams(), model, attr);
}
@ -157,13 +154,13 @@ public class PolicyPageController extends PageController<NoPageParams> {
* @param attr RedirectAttributes used to forward data back to the original
* page.
* @return View containing the url and parameters
* @throws URISyntaxException if malformed URI
* @throws java.net.URISyntaxException if malformed URI
*/
@GetMapping
@RequestMapping(value = "update-pc-attribute-validation", method = RequestMethod.POST)
public RedirectView updatePcAttributeVal(@ModelAttribute final PolicyPageModel ppModel,
final RedirectAttributes attr)
throws URISyntaxException {
Map<String, Object> model = new HashMap<>();
PageMessages messages = new PageMessages();
String successMessage;
@ -171,7 +168,7 @@ public class PolicyPageController extends PageController<NoPageParams> {
.equalsIgnoreCase(ENABLED_CHECKED_PARAMETER_VALUE);
try {
SupplyChainPolicy policy = getDefaultPolicyAndSetInModel(ppModel, model);
SupplyChainSettings policy = getDefaultPolicyAndSetInModel(ppModel, model);
// If PC Attribute Validation is enabled without PC Validation, disallow change
if (!isPolicyValid(policy.isEcValidationEnabled(),
@ -191,12 +188,11 @@ public class PolicyPageController extends PageController<NoPageParams> {
successMessage = "Platform certificate attribute validation disabled";
}
savePolicyAndApplySuccessMessage(ppModel, model, messages, successMessage, policy);
} catch (PolicyManagerException e) {
} catch (PolicyManagerException pmEx) {
// Log and return any error messages to the user
handlePolicyManagerUpdateError(model, messages, e,
handlePolicyManagerUpdateError(model, messages, pmEx,
"Error changing ACA platform certificate attribute validation policy",
"Error updating policy. \n" + e.getMessage());
"Error updating policy. \n" + pmEx.getMessage());
}
return redirectToSelf(new NoPageParams(), model, attr);
}
@ -208,7 +204,7 @@ public class PolicyPageController extends PageController<NoPageParams> {
* @param ppModel The data posted by the form mapped into an object.
* @param attr RedirectAttributes used to forward data back to the original page.
* @return View containing the url and parameters
* @throws URISyntaxException if malformed URI
* @throws java.net.URISyntaxException if malformed URI
*/
@RequestMapping(value = "update-issue-attestation", method = RequestMethod.POST)
public RedirectView updateAttestationVal(@ModelAttribute final PolicyPageModel ppModel,
@ -224,7 +220,7 @@ public class PolicyPageController extends PageController<NoPageParams> {
.equalsIgnoreCase(ENABLED_CHECKED_PARAMETER_VALUE);
try {
SupplyChainPolicy policy = getDefaultPolicyAndSetInModel(ppModel, model);
SupplyChainSettings policy = getDefaultPolicyAndSetInModel(ppModel, model);
if (issuedAttestationOptionEnabled) {
successMessage = "Attestation Certificate generation enabled.";
@ -235,10 +231,10 @@ public class PolicyPageController extends PageController<NoPageParams> {
policy.setIssueAttestationCertificate(issuedAttestationOptionEnabled);
savePolicyAndApplySuccessMessage(ppModel, model, messages, successMessage, policy);
} catch (PolicyManagerException e) {
handlePolicyManagerUpdateError(model, messages, e,
} catch (PolicyManagerException pmEx) {
handlePolicyManagerUpdateError(model, messages, pmEx,
"Error changing ACA Attestation Certificate generation policy",
"Error updating policy. \n" + e.getMessage());
"Error updating policy. \n" + pmEx.getMessage());
}
// return the redirect
@ -252,7 +248,7 @@ public class PolicyPageController extends PageController<NoPageParams> {
* @param ppModel The data posted by the form mapped into an object.
* @param attr RedirectAttributes used to forward data back to the original page.
* @return View containing the url and parameters
* @throws URISyntaxException if malformed URI
* @throws java.net.URISyntaxException if malformed URI
*/
@RequestMapping(value = "update-issue-devid", method = RequestMethod.POST)
public RedirectView updateDevIdVal(@ModelAttribute final PolicyPageModel ppModel,
@ -268,7 +264,7 @@ public class PolicyPageController extends PageController<NoPageParams> {
.equalsIgnoreCase(ENABLED_CHECKED_PARAMETER_VALUE);
try {
SupplyChainPolicy policy = getDefaultPolicyAndSetInModel(ppModel, model);
SupplyChainSettings policy = getDefaultPolicyAndSetInModel(ppModel, model);
if (issuedDevIdOptionEnabled) {
successMessage = "DevID Certificate generation enabled.";
@ -279,10 +275,10 @@ public class PolicyPageController extends PageController<NoPageParams> {
policy.setIssueDevIdCertificate(issuedDevIdOptionEnabled);
savePolicyAndApplySuccessMessage(ppModel, model, messages, successMessage, policy);
} catch (PolicyManagerException e) {
handlePolicyManagerUpdateError(model, messages, e,
} catch (PolicyManagerException pmEx) {
handlePolicyManagerUpdateError(model, messages, pmEx,
"Error changing ACA DevID Certificate generation policy",
"Error updating policy. \n" + e.getMessage());
"Error updating policy. \n" + pmEx.getMessage());
}
// return the redirect
@ -297,7 +293,7 @@ public class PolicyPageController extends PageController<NoPageParams> {
* @param ppModel The data posted by the form mapped into an object.
* @param attr RedirectAttributes used to forward data back to the original page.
* @return View containing the url and parameters
* @throws URISyntaxException if malformed URI
* @throws java.net.URISyntaxException if malformed URI
*/
@RequestMapping(value = "update-expire-on", method = RequestMethod.POST)
public RedirectView updateExpireOnVal(@ModelAttribute final PolicyPageModel ppModel,
@ -320,7 +316,7 @@ public class PolicyPageController extends PageController<NoPageParams> {
}
try {
SupplyChainPolicy policy = getDefaultPolicyAndSetInModel(ppModel, model);
SupplyChainSettings policy = getDefaultPolicyAndSetInModel(ppModel, model);
boolean issuedAttestationOptionEnabled
= policy.isIssueAttestationCertificate();
@ -334,7 +330,7 @@ public class PolicyPageController extends PageController<NoPageParams> {
if (generateCertificateEnabled) {
numOfDays = ppModel.getExpirationValue();
if (numOfDays == null) {
numOfDays = SupplyChainPolicy.TEN_YEARS;
numOfDays = SupplyChainSettings.TEN_YEARS;
}
} else {
numOfDays = policy.getValidityDays();
@ -349,10 +345,10 @@ public class PolicyPageController extends PageController<NoPageParams> {
policy.setGenerateOnExpiration(generateCertificateEnabled);
savePolicyAndApplySuccessMessage(ppModel, model, messages, successMessage, policy);
} catch (PolicyManagerException e) {
handlePolicyManagerUpdateError(model, messages, e,
} catch (PolicyManagerException pmEx) {
handlePolicyManagerUpdateError(model, messages, pmEx,
"Error changing ACA Attestation Certificate generation policy",
"Error updating policy. \n" + e.getMessage());
"Error updating policy. \n" + pmEx.getMessage());
}
// return the redirect
@ -367,7 +363,7 @@ public class PolicyPageController extends PageController<NoPageParams> {
* @param ppModel The data posted by the form mapped into an object.
* @param attr RedirectAttributes used to forward data back to the original page.
* @return View containing the url and parameters
* @throws URISyntaxException if malformed URI
* @throws java.net.URISyntaxException if malformed URI
*/
@RequestMapping(value = "update-devid-expire-on", method = RequestMethod.POST)
public RedirectView updateDevIdExpireOnVal(@ModelAttribute final PolicyPageModel ppModel,
@ -390,7 +386,7 @@ public class PolicyPageController extends PageController<NoPageParams> {
}
try {
SupplyChainPolicy policy = getDefaultPolicyAndSetInModel(ppModel, model);
SupplyChainSettings policy = getDefaultPolicyAndSetInModel(ppModel, model);
boolean issuedDevIdOptionEnabled
= policy.isIssueDevIdCertificate();
@ -404,7 +400,7 @@ public class PolicyPageController extends PageController<NoPageParams> {
if (generateDevIdCertificateEnabled) {
numOfDays = ppModel.getDevIdExpirationValue();
if (numOfDays == null) {
numOfDays = SupplyChainPolicy.TEN_YEARS;
numOfDays = SupplyChainSettings.TEN_YEARS;
}
} else {
numOfDays = policy.getDevIdValidityDays();
@ -419,10 +415,10 @@ public class PolicyPageController extends PageController<NoPageParams> {
policy.setDevIdExpirationFlag(generateDevIdCertificateEnabled);
savePolicyAndApplySuccessMessage(ppModel, model, messages, successMessage, policy);
} catch (PolicyManagerException e) {
handlePolicyManagerUpdateError(model, messages, e,
} catch (PolicyManagerException pmEx) {
handlePolicyManagerUpdateError(model, messages, pmEx,
"Error changing ACA DevID Certificate generation policy",
"Error updating policy. \n" + e.getMessage());
"Error updating policy. \n" + pmEx.getMessage());
}
// return the redirect
@ -437,7 +433,7 @@ public class PolicyPageController extends PageController<NoPageParams> {
* @param ppModel The data posted by the form mapped into an object.
* @param attr RedirectAttributes used to forward data back to the original page.
* @return View containing the url and parameters
* @throws URISyntaxException if malformed URI
* @throws java.net.URISyntaxException if malformed URI
*/
@RequestMapping(value = "update-threshold", method = RequestMethod.POST)
public RedirectView updateThresholdVal(@ModelAttribute final PolicyPageModel ppModel,
@ -460,7 +456,7 @@ public class PolicyPageController extends PageController<NoPageParams> {
}
try {
SupplyChainPolicy policy = getDefaultPolicyAndSetInModel(ppModel, model);
SupplyChainSettings policy = getDefaultPolicyAndSetInModel(ppModel, model);
boolean issuedAttestationOptionEnabled
= policy.isIssueAttestationCertificate();
@ -478,7 +474,7 @@ public class PolicyPageController extends PageController<NoPageParams> {
}
if (threshold == null || threshold.isEmpty()) {
threshold = SupplyChainPolicy.YEAR;
threshold = SupplyChainSettings.YEAR;
}
policy.setReissueThreshold(threshold);
@ -490,10 +486,10 @@ public class PolicyPageController extends PageController<NoPageParams> {
policy.setGenerateOnExpiration(generateCertificateEnabled);
savePolicyAndApplySuccessMessage(ppModel, model, messages, successMessage, policy);
} catch (PolicyManagerException e) {
handlePolicyManagerUpdateError(model, messages, e,
} catch (PolicyManagerException pmEx) {
handlePolicyManagerUpdateError(model, messages, pmEx,
"Error changing ACA Attestation Certificate generation policy",
"Error updating policy. \n" + e.getMessage());
"Error updating policy. \n" + pmEx.getMessage());
}
// return the redirect
@ -508,7 +504,7 @@ public class PolicyPageController extends PageController<NoPageParams> {
* @param ppModel The data posted by the form mapped into an object.
* @param attr RedirectAttributes used to forward data back to the original page.
* @return View containing the url and parameters
* @throws URISyntaxException if malformed URI
* @throws java.net.URISyntaxException if malformed URI
*/
@RequestMapping(value = "update-devid-threshold", method = RequestMethod.POST)
public RedirectView updateDevIdThresholdVal(@ModelAttribute final PolicyPageModel ppModel,
@ -530,7 +526,7 @@ public class PolicyPageController extends PageController<NoPageParams> {
}
try {
SupplyChainPolicy policy = getDefaultPolicyAndSetInModel(ppModel, model);
SupplyChainSettings policy = getDefaultPolicyAndSetInModel(ppModel, model);
boolean issuedDevIdOptionEnabled
= policy.isIssueDevIdCertificate();
@ -548,7 +544,7 @@ public class PolicyPageController extends PageController<NoPageParams> {
}
if (threshold == null || threshold.isEmpty()) {
threshold = SupplyChainPolicy.YEAR;
threshold = SupplyChainSettings.YEAR;
}
policy.setDevIdReissueThreshold(threshold);
@ -560,10 +556,10 @@ public class PolicyPageController extends PageController<NoPageParams> {
policy.setDevIdExpirationFlag(generateDevIdCertificateEnabled);
savePolicyAndApplySuccessMessage(ppModel, model, messages, successMessage, policy);
} catch (PolicyManagerException e) {
handlePolicyManagerUpdateError(model, messages, e,
} catch (PolicyManagerException pmEx) {
handlePolicyManagerUpdateError(model, messages, pmEx,
"Error changing ACA DevID Certificate generation policy",
"Error updating policy. \n" + e.getMessage());
"Error updating policy. \n" + pmEx.getMessage());
}
// return the redirect
@ -578,7 +574,7 @@ public class PolicyPageController extends PageController<NoPageParams> {
* @param attr RedirectAttributes used to forward data back to the original
* page.
* @return View containing the url and parameters
* @throws URISyntaxException if malformed URI
* @throws java.net.URISyntaxException if malformed URI
*/
@RequestMapping(value = "update-ec-validation", method = RequestMethod.POST)
public RedirectView updateEcVal(@ModelAttribute final PolicyPageModel ppModel,
@ -592,7 +588,7 @@ public class PolicyPageController extends PageController<NoPageParams> {
= ppModel.getEcValidate().equalsIgnoreCase(ENABLED_CHECKED_PARAMETER_VALUE);
try {
SupplyChainPolicy policy = getDefaultPolicyAndSetInModel(ppModel, model);
SupplyChainSettings policy = getDefaultPolicyAndSetInModel(ppModel, model);
//If PC Validation is enabled without EC Validation, disallow change
if (!isPolicyValid(ecValidationOptionEnabled, policy.isPcValidationEnabled(),
@ -612,10 +608,10 @@ public class PolicyPageController extends PageController<NoPageParams> {
}
savePolicyAndApplySuccessMessage(ppModel, model, messages, successMessage, policy);
} catch (PolicyManagerException e) {
handlePolicyManagerUpdateError(model, messages, e,
} catch (PolicyManagerException pmEx) {
handlePolicyManagerUpdateError(model, messages, pmEx,
"Error changing ACA endorsement validation policy",
"Error updating policy. \n" + e.getMessage());
"Error updating policy. \n" + pmEx.getMessage());
}
// return the redirect
@ -630,7 +626,7 @@ public class PolicyPageController extends PageController<NoPageParams> {
* @param attr RedirectAttributes used to forward data back to the original
* page.
* @return View containing the url and parameters
* @throws URISyntaxException if malformed URI
* @throws java.net.URISyntaxException if malformed URI
*/
@RequestMapping(value = "update-firmware-validation", method = RequestMethod.POST)
public RedirectView updateFirmwareVal(@ModelAttribute final PolicyPageModel ppModel,
@ -644,7 +640,7 @@ public class PolicyPageController extends PageController<NoPageParams> {
.equalsIgnoreCase(ENABLED_CHECKED_PARAMETER_VALUE);
try {
SupplyChainPolicy policy = getDefaultPolicyAndSetInModel(ppModel, model);
SupplyChainSettings policy = getDefaultPolicyAndSetInModel(ppModel, model);
//If firmware is enabled without PC attributes, disallow change
if (firmwareValidationOptionEnabled && !policy.isPcAttributeValidationEnabled()) {
@ -661,17 +657,17 @@ public class PolicyPageController extends PageController<NoPageParams> {
successMessage = "Firmware validation enabled";
} else {
policy.setFirmwareValidationEnabled(false);
policy.getPcrPolicy().setEnableIgnoreIma(false);
policy.getPcrPolicy().setEnableIgnoretBoot(false);
policy.setIgnoreImaEnabled(false);
policy.setIgnoretBootEnabled(false);
policy.setIgnoreOsEvtEnabled(false);
successMessage = "Firmware validation disabled";
}
savePolicyAndApplySuccessMessage(ppModel, model, messages, successMessage, policy);
} catch (PolicyManagerException e) {
handlePolicyManagerUpdateError(model, messages, e,
} catch (PolicyManagerException pmEx) {
handlePolicyManagerUpdateError(model, messages, pmEx,
"Error changing ACA firmware validation policy",
"Error updating policy. \n" + e.getMessage());
"Error updating policy. \n" + pmEx.getMessage());
}
@ -687,7 +683,7 @@ public class PolicyPageController extends PageController<NoPageParams> {
* @param attr RedirectAttributes used to forward data back to the original
* page.
* @return View containing the url and parameters
* @throws URISyntaxException if malformed URI
* @throws java.net.URISyntaxException if malformed URI
*/
@RequestMapping(value = "update-ima-ignore", method = RequestMethod.POST)
public RedirectView updateIgnoreIma(@ModelAttribute final PolicyPageModel ppModel,
@ -700,7 +696,7 @@ public class PolicyPageController extends PageController<NoPageParams> {
.equalsIgnoreCase(ENABLED_CHECKED_PARAMETER_VALUE);
try {
SupplyChainPolicy policy = getDefaultPolicyAndSetInModel(ppModel, model);
SupplyChainSettings policy = getDefaultPolicyAndSetInModel(ppModel, model);
//If Ignore IMA is enabled without firmware, disallow change
if (ignoreImaOptionEnabled && !policy.isFirmwareValidationEnabled()) {
@ -712,18 +708,18 @@ public class PolicyPageController extends PageController<NoPageParams> {
// set the policy option and create success message
if (ignoreImaOptionEnabled) {
policy.getPcrPolicy().setEnableIgnoreIma(true);
policy.setIgnoreImaEnabled(true);
successMessage = "Ignore IMA enabled";
} else {
policy.getPcrPolicy().setEnableIgnoreIma(false);
policy.setIgnoreImaEnabled(false);
successMessage = "Ignore IMA disabled";
}
savePolicyAndApplySuccessMessage(ppModel, model, messages, successMessage, policy);
} catch (PolicyManagerException e) {
handlePolicyManagerUpdateError(model, messages, e,
} catch (PolicyManagerException pmEx) {
handlePolicyManagerUpdateError(model, messages, pmEx,
"Error changing ACA IMA ignore policy",
"Error updating policy. \n" + e.getMessage());
"Error updating policy. \n" + pmEx.getMessage());
}
// return the redirect
@ -738,7 +734,7 @@ public class PolicyPageController extends PageController<NoPageParams> {
* @param attr RedirectAttributes used to forward data back to the original
* page.
* @return View containing the url and parameters
* @throws URISyntaxException if malformed URI
* @throws java.net.URISyntaxException if malformed URI
*/
@RequestMapping(value = "update-tboot-ignore", method = RequestMethod.POST)
public RedirectView updateIgnoreTboot(@ModelAttribute final PolicyPageModel ppModel,
@ -751,7 +747,7 @@ public class PolicyPageController extends PageController<NoPageParams> {
.equalsIgnoreCase(ENABLED_CHECKED_PARAMETER_VALUE);
try {
SupplyChainPolicy policy = getDefaultPolicyAndSetInModel(ppModel, model);
SupplyChainSettings policy = getDefaultPolicyAndSetInModel(ppModel, model);
//If Ignore TBoot is enabled without firmware, disallow change
if (ignoreTbootOptionEnabled && !policy.isFirmwareValidationEnabled()) {
@ -763,18 +759,18 @@ public class PolicyPageController extends PageController<NoPageParams> {
// set the policy option and create success message
if (ignoreTbootOptionEnabled) {
policy.getPcrPolicy().setEnableIgnoretBoot(true);
policy.setIgnoretBootEnabled(true);
successMessage = "Ignore TBoot enabled";
} else {
policy.getPcrPolicy().setEnableIgnoretBoot(false);
policy.setIgnoretBootEnabled(false);
successMessage = "Ignore TBoot disabled";
}
savePolicyAndApplySuccessMessage(ppModel, model, messages, successMessage, policy);
} catch (PolicyManagerException e) {
handlePolicyManagerUpdateError(model, messages, e,
} catch (PolicyManagerException pmEx) {
handlePolicyManagerUpdateError(model, messages, pmEx,
"Error changing ACA TBoot ignore policy",
"Error updating policy. \n" + e.getMessage());
"Error updating policy. \n" + pmEx.getMessage());
}
// return the redirect
@ -789,7 +785,7 @@ public class PolicyPageController extends PageController<NoPageParams> {
* @param attr RedirectAttributes used to forward data back to the original
* page.
* @return View containing the url and parameters
* @throws URISyntaxException if malformed URI
* @throws java.net.URISyntaxException if malformed URI
*/
@RequestMapping(value = "update-gpt-ignore", method = RequestMethod.POST)
public RedirectView updateIgnoreGptEvents(@ModelAttribute final PolicyPageModel ppModel,
@ -802,7 +798,7 @@ public class PolicyPageController extends PageController<NoPageParams> {
.equalsIgnoreCase(ENABLED_CHECKED_PARAMETER_VALUE);
try {
SupplyChainPolicy policy = getDefaultPolicyAndSetInModel(ppModel, model);
SupplyChainSettings policy = getDefaultPolicyAndSetInModel(ppModel, model);
//If Ignore TBoot is enabled without firmware, disallow change
if (ignoreGptOptionEnabled && !policy.isFirmwareValidationEnabled()) {
@ -814,18 +810,18 @@ public class PolicyPageController extends PageController<NoPageParams> {
// set the policy option and create success message
if (ignoreGptOptionEnabled) {
policy.getPcrPolicy().setEnableIgnoreGpt(true);
policy.setIgnoreGptEnabled(true);
successMessage = "Ignore GPT enabled";
} else {
policy.getPcrPolicy().setEnableIgnoreGpt(false);
policy.setIgnoreGptEnabled(false);
successMessage = "Ignore GPT disabled";
}
savePolicyAndApplySuccessMessage(ppModel, model, messages, successMessage, policy);
} catch (PolicyManagerException e) {
handlePolicyManagerUpdateError(model, messages, e,
} catch (PolicyManagerException pmEx) {
handlePolicyManagerUpdateError(model, messages, pmEx,
"Error changing ACA GPT ignore policy",
"Error updating policy. \n" + e.getMessage());
"Error updating policy. \n" + pmEx.getMessage());
}
// return the redirect
@ -840,7 +836,7 @@ public class PolicyPageController extends PageController<NoPageParams> {
* @param attr RedirectAttributes used to forward data back to the original
* page.
* @return View containing the url and parameters
* @throws URISyntaxException if malformed URI
* @throws java.net.URISyntaxException if malformed URI
*/
@RequestMapping(value = "update-os-evt-ignore", method = RequestMethod.POST)
public RedirectView updateIgnoreOsEvents(
@ -855,7 +851,7 @@ public class PolicyPageController extends PageController<NoPageParams> {
.equalsIgnoreCase(ENABLED_CHECKED_PARAMETER_VALUE);
try {
SupplyChainPolicy policy = getDefaultPolicyAndSetInModel(ppModel, model);
SupplyChainSettings policy = getDefaultPolicyAndSetInModel(ppModel, model);
//If Ignore TBoot is enabled without firmware, disallow change
if (ignoreOsEvtOptionEnabled && !policy.isFirmwareValidationEnabled()) {
@ -867,19 +863,19 @@ public class PolicyPageController extends PageController<NoPageParams> {
// set the policy option and create success message
if (ignoreOsEvtOptionEnabled) {
policy.getPcrPolicy().setEnableIgnoreOsEvt(true);
policy.getPcrPolicy().setEnableIgnoreGpt(true);
policy.setIgnoreOsEvtEnabled(true);
policy.setIgnoreGptEnabled(true);
successMessage = "Ignore OS Events enabled";
} else {
policy.getPcrPolicy().setEnableIgnoreOsEvt(false);
policy.setIgnoreOsEvtEnabled(false);
successMessage = "Ignore OS Events disabled";
}
savePolicyAndApplySuccessMessage(ppModel, model, messages, successMessage, policy);
} catch (PolicyManagerException e) {
handlePolicyManagerUpdateError(model, messages, e,
} catch (PolicyManagerException pmEx) {
handlePolicyManagerUpdateError(model, messages, pmEx,
"Error changing ACA OS Events ignore policy",
"Error updating policy. \n" + e.getMessage());
"Error updating policy. \n" + pmEx.getMessage());
}
// return the redirect
@ -888,9 +884,9 @@ public class PolicyPageController extends PageController<NoPageParams> {
private void handlePolicyManagerUpdateError(final Map<String, Object> model,
final PageMessages messages,
final PolicyManagerException e,
final PolicyManagerException pmEx,
final String message, final String error) {
LOGGER.error(message, e);
LOGGER.error(message, pmEx);
messages.addError(error);
model.put(MESSAGES_ATTRIBUTE, messages);
}
@ -914,7 +910,6 @@ public class PolicyPageController extends PageController<NoPageParams> {
*/
private static boolean isPolicyValid(final boolean isEcEnable, final boolean isPcEnable,
final boolean isPcAttEnable) {
if (isPcAttEnable && !isPcEnable) {
return false;
} else {
@ -927,11 +922,12 @@ public class PolicyPageController extends PageController<NoPageParams> {
*
* @return The default Supply Chain Policy
*/
private SupplyChainPolicy getDefaultPolicy() {
final Appraiser supplyChainAppraiser = appraiserManager.getAppraiser(
SupplyChainAppraiser.NAME);
return (SupplyChainPolicy) policyManager.getDefaultPolicy(
supplyChainAppraiser);
private SupplyChainSettings getDefaultPolicy() {
// final Appraiser supplyChainAppraiser = appraiserService.getAppraiser(
// SupplyChainAppraiser.NAME);
return new SupplyChainSettings("Default");
// return (SupplyChainSettings) policyService.getDefaultPolicy(
// supplyChainAppraiser);
}
/**
@ -942,23 +938,22 @@ public class PolicyPageController extends PageController<NoPageParams> {
* @param model the map of string messages to be displayed on the view
* @return The default Supply Chain Policy
*/
private SupplyChainPolicy getDefaultPolicyAndSetInModel(
private SupplyChainSettings getDefaultPolicyAndSetInModel(
final PolicyPageModel ppModel, final Map<String, Object> model) {
// load the current default policy from the DB
SupplyChainPolicy policy = getDefaultPolicy();
SupplyChainSettings policy = getDefaultPolicy();
// set the data received to be populated back into the form
model.put(RESULT_DATA, ppModel);
return policy;
}
private void savePolicyAndApplySuccessMessage(final PolicyPageModel ppModel,
final Map<String, Object> model,
final PageMessages messages,
final String successMessage,
final SupplyChainPolicy policy) {
private void savePolicyAndApplySuccessMessage(
final PolicyPageModel ppModel, final Map<String, Object> model,
final PageMessages messages, final String successMessage,
final SupplyChainSettings settings) {
// save the policy to the DB
policyManager.updatePolicy(policy);
settingsService.updateSettings(settings);
// Log and set the success message
messages.addSuccess(successMessage);
@ -967,3 +962,4 @@ public class PolicyPageController extends PageController<NoPageParams> {
model.put(MESSAGES_ATTRIBUTE, messages);
}
}

View File

@ -1,611 +0,0 @@
package hirs.attestationca.portal.page.controllers;
import hirs.attestationca.portal.page.Page;
import hirs.attestationca.portal.page.PageController;
import hirs.attestationca.portal.page.PageMessages;
import hirs.attestationca.portal.page.params.ReferenceManifestDetailsPageParams;
import hirs.attestationca.service.SupplyChainValidationServiceImpl;
import hirs.data.persist.BaseReferenceManifest;
import hirs.data.persist.EventLogMeasurements;
import hirs.data.persist.ReferenceDigestValue;
import hirs.data.persist.ReferenceManifest;
import hirs.data.persist.SupportReferenceManifest;
import hirs.data.persist.SwidResource;
import hirs.data.persist.certificate.CertificateAuthorityCredential;
import hirs.persist.CertificateManager;
import hirs.persist.DBManagerException;
import hirs.persist.ReferenceDigestManager;
import hirs.persist.ReferenceEventManager;
import hirs.persist.ReferenceManifestManager;
import hirs.tpm.eventlog.TCGEventLog;
import hirs.tpm.eventlog.TpmPcrEvent;
import hirs.utils.ReferenceManifestValidator;
import hirs.validation.SupplyChainCredentialValidator;
import hirs.validation.SupplyChainValidatorException;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import java.io.IOException;
import java.security.KeyStore;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
/**
* Controller for the Reference Manifest Details page.
*/
@Controller
@RequestMapping("/rim-details")
public class ReferenceManifestDetailsPageController
extends PageController<ReferenceManifestDetailsPageParams> {
private final ReferenceManifestManager referenceManifestManager;
private final ReferenceDigestManager referenceDigestManager;
private final ReferenceEventManager referenceEventManager;
private final CertificateManager certificateManager;
private static final ReferenceManifestValidator RIM_VALIDATOR
= new ReferenceManifestValidator();
private static final Logger LOGGER
= LogManager.getLogger(ReferenceManifestDetailsPageController.class);
/**
* 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.
*/
@Autowired
public ReferenceManifestDetailsPageController(
final ReferenceManifestManager referenceManifestManager,
final ReferenceDigestManager referenceDigestManager,
final ReferenceEventManager referenceEventManager,
final CertificateManager certificateManager) {
super(Page.RIM_DETAILS);
this.referenceManifestManager = referenceManifestManager;
this.referenceDigestManager = referenceDigestManager;
this.referenceEventManager = referenceEventManager;
this.certificateManager = certificateManager;
}
/**
* Returns the filePath for the view and the data model for the page.
*
* @param params The object to map url parameters into.
* @param model The data model for the request. Can contain data from
* redirect.
* @return the path for the view and data model for the page.
*/
@Override
public ModelAndView initPage(final ReferenceManifestDetailsPageParams params,
final Model model) {
// get the basic information to render the page
ModelAndView mav = getBaseModelAndView();
PageMessages messages = new PageMessages();
// Map with the rim information
HashMap<String, Object> data = new HashMap<>();
// Check if parameters were set
if (params.getId() == null) {
String typeError = "ID was not provided";
messages.addError(typeError);
LOGGER.debug(typeError);
mav.addObject(MESSAGES_ATTRIBUTE, messages);
} else {
try {
UUID uuid = UUID.fromString(params.getId());
data.putAll(getRimDetailInfo(uuid, referenceManifestManager,
referenceDigestManager, referenceEventManager, certificateManager));
} catch (IllegalArgumentException iaEx) {
String uuidError = "Failed to parse ID from: " + params.getId();
messages.addError(uuidError);
LOGGER.error(uuidError, iaEx);
} catch (Exception ioEx) {
LOGGER.error(ioEx);
}
if (data.isEmpty()) {
String notFoundMessage = "Unable to find RIM with ID: " + params.getId();
messages.addError(notFoundMessage);
LOGGER.warn(notFoundMessage);
mav.addObject(MESSAGES_ATTRIBUTE, messages);
} else {
mav.addObject(INITIAL_DATA, data);
}
}
// return the model and view
return mav;
}
/**
* This method takes the place of an entire class for a string builder.
* Gathers all information and returns it for displays.
*
* @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.
* @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<String, Object> getRimDetailInfo(final UUID uuid,
final ReferenceManifestManager referenceManifestManager,
final ReferenceDigestManager referenceDigestManager,
final ReferenceEventManager referenceEventManager,
final CertificateManager certificateManager)
throws IOException,
CertificateException, NoSuchAlgorithmException {
HashMap<String, Object> data = new HashMap<>();
BaseReferenceManifest bRim = BaseReferenceManifest.select(referenceManifestManager)
.byEntityId(uuid).getRIM();
if (bRim != null) {
data.putAll(getBaseRimInfo(bRim, referenceManifestManager, certificateManager));
}
SupportReferenceManifest sRim = SupportReferenceManifest.select(referenceManifestManager)
.byEntityId(uuid).getRIM();
if (sRim != null) {
data.putAll(getSupportRimInfo(sRim, referenceManifestManager));
}
EventLogMeasurements bios = EventLogMeasurements.select(referenceManifestManager)
.byEntityId(uuid).getRIM();
if (bios != null) {
data.putAll(getMeasurementsRimInfo(bios, referenceManifestManager,
referenceDigestManager, referenceEventManager));
}
return data;
}
/**
* 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 referenceManifestManager the reference manifest 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.
*/
private static HashMap<String, Object> getBaseRimInfo(
final BaseReferenceManifest baseRim,
final ReferenceManifestManager referenceManifestManager,
final CertificateManager certificateManager)
throws IOException, CertificateException, NoSuchAlgorithmException {
HashMap<String, Object> data = new HashMap<>();
// Software Identity
data.put("swidName", baseRim.getSwidName());
data.put("swidVersion", baseRim.getSwidVersion());
data.put("swidTagVersion", baseRim.getSwidTagVersion());
if (baseRim.isSwidCorpus() == 1) {
data.put("swidCorpus", "True");
} else {
data.put("swidCorpus", "False");
}
if (baseRim.isSwidPatch()) {
data.put("swidPatch", "True");
} else {
data.put("swidPatch", "False");
}
if (baseRim.isSwidSupplemental()) {
data.put("swidSupplemental", "True");
} else {
data.put("swidSupplemental", "False");
}
data.put("swidTagId", baseRim.getTagId());
// Entity
data.put("entityName", baseRim.getEntityName());
data.put("entityRegId", baseRim.getEntityRegId());
data.put("entityRole", baseRim.getEntityRole());
data.put("entityThumbprint", baseRim.getEntityThumbprint());
// Link
data.put("linkHref", baseRim.getLinkHref());
data.put("linkHrefLink", "");
for (BaseReferenceManifest bRim : BaseReferenceManifest
.select(referenceManifestManager).getRIMs()) {
if (baseRim.getLinkHref().contains(bRim.getTagId())) {
data.put("linkHrefLink", bRim.getId());
}
}
data.put("linkRel", baseRim.getLinkRel());
data.put("platformManufacturer", baseRim.getPlatformManufacturer());
data.put("platformManufacturerId", baseRim.getPlatformManufacturerId());
data.put("platformModel", baseRim.getPlatformModel());
data.put("platformVersion", baseRim.getPlatformVersion());
data.put("payloadType", baseRim.getPayloadType());
data.put("colloquialVersion", baseRim.getColloquialVersion());
data.put("edition", baseRim.getEdition());
data.put("product", baseRim.getProduct());
data.put("revision", baseRim.getRevision());
data.put("bindingSpec", baseRim.getBindingSpec());
data.put("bindingSpecVersion", baseRim.getBindingSpecVersion());
data.put("pcUriGlobal", baseRim.getPcURIGlobal());
data.put("pcUriLocal", baseRim.getPcURILocal());
data.put("rimLinkHash", baseRim.getRimLinkHash());
if (baseRim.getRimLinkHash() != null) {
ReferenceManifest rim = BaseReferenceManifest.select(referenceManifestManager)
.byHexDecHash(baseRim.getRimLinkHash()).getRIM();
if (rim != null) {
data.put("rimLinkId", rim.getId());
data.put("linkHashValid", true);
} else {
data.put("linkHashValid", false);
}
}
data.put("rimType", baseRim.getRimType());
List<SwidResource> resources = baseRim.parseResource();
TCGEventLog logProcessor = null;
SupportReferenceManifest support = null;
if (baseRim.getAssociatedRim() == null) {
support = SupportReferenceManifest.select(referenceManifestManager)
.byManufacturer(baseRim.getPlatformManufacturer())
.getRIM();
if (support != null) {
baseRim.setAssociatedRim(support.getId());
}
} else {
support = SupportReferenceManifest.select(referenceManifestManager)
.byEntityId(baseRim.getAssociatedRim()).getRIM();
}
// 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())) {
RIM_VALIDATOR.validateSupportRimHash(support.getRimBytes(),
swidRes.getHashValue());
if (RIM_VALIDATOR.isSupportRimValid()) {
data.put("supportRimHashValid", true);
} else {
data.put("supportRimHashValid", false);
}
break;
}
}
data.put("associatedRim", baseRim.getAssociatedRim());
data.put("swidFiles", resources);
if (support != null && (!baseRim.isSwidSupplemental()
&& !baseRim.isSwidPatch())) {
data.put("pcrList", support.getExpectedPCRList());
}
Set<CertificateAuthorityCredential> certificates =
CertificateAuthorityCredential.select(certificateManager)
.getCertificates();
//Report invalid signature unless RIM_VALIDATOR validates it and cert path is valid
data.put("signatureValid", false);
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;
}
} catch (SupplyChainValidatorException e) {
LOGGER.error("Error verifying cert chain: " + e.getMessage());
}
}
}
data.put("skID", RIM_VALIDATOR.getSubjectKeyIdentifier());
try {
for (CertificateAuthorityCredential cert : certificates) {
if (Arrays.equals(cert.getEncodedPublicKey(),
RIM_VALIDATOR.getPublicKey().getEncoded())) {
data.put("issuerID", cert.getId().toString());
}
}
} catch (NullPointerException e) {
LOGGER.error("Unable to link signing certificate: " + e.getMessage());
}
return data;
}
/**
* 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 referenceManifestManager the reference manifest 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.
*/
private static HashMap<String, Object> getSupportRimInfo(
final SupportReferenceManifest support,
final ReferenceManifestManager referenceManifestManager)
throws IOException, CertificateException, NoSuchAlgorithmException {
HashMap<String, Object> data = new HashMap<>();
EventLogMeasurements measurements = null;
if (support.getAssociatedRim() == null) {
Set<BaseReferenceManifest> baseRims = BaseReferenceManifest
.select(referenceManifestManager)
.byRimType(ReferenceManifest.BASE_RIM).getRIMs();
for (BaseReferenceManifest baseRim : baseRims) {
if (baseRim != null && baseRim.getAssociatedRim() != null
&& baseRim.getAssociatedRim().equals(support.getId())) {
support.setAssociatedRim(baseRim.getId());
try {
referenceManifestManager.update(support);
} catch (DBManagerException ex) {
LOGGER.error("Failed to update Support RIM", ex);
}
break;
}
}
}
// testing this independent of the above if statement because the above
// starts off checking if associated rim is null; that is irrelevant for
// this statement.
measurements = EventLogMeasurements.select(referenceManifestManager)
.byHexDecHash(support.getHexDecHash()).getRIM();
if (support.isSwidPatch()) {
data.put("swidPatch", "True");
} else {
data.put("swidPatch", "False");
}
if (support.isSwidSupplemental()) {
data.put("swidSupplemental", "True");
} else {
data.put("swidSupplemental", "False");
}
data.put("swidBase", (!support.isSwidPatch()
&& !support.isSwidSupplemental()));
data.put("baseRim", support.getTagId());
data.put("associatedRim", support.getAssociatedRim());
data.put("rimType", support.getRimType());
data.put("tagId", support.getTagId());
TCGEventLog logProcessor = new TCGEventLog(support.getRimBytes());
LinkedList<TpmPcrEvent> tpmPcrEvents = new LinkedList<>();
TCGEventLog measurementsProcess;
if (measurements != null) {
measurementsProcess = new TCGEventLog((measurements.getRimBytes()));
HashMap<String, TpmPcrEvent> digestMap = new HashMap<>();
for (TpmPcrEvent tpe : logProcessor.getEventList()) {
digestMap.put(tpe.getEventDigestStr(), tpe);
if (!support.isSwidSupplemental()
&& !tpe.eventCompare(
measurementsProcess.getEventByNumber(
tpe.getEventNumber()))) {
tpe.setError(true);
}
tpmPcrEvents.add(tpe);
}
for (TpmPcrEvent tpe : logProcessor.getEventList()) {
tpe.setError(!digestMap.containsKey(tpe.getEventDigestStr()));
}
data.put("events", tpmPcrEvents);
} else {
data.put("events", logProcessor.getEventList());
}
getEventSummary(data, logProcessor.getEventList());
return data;
}
private static void getEventSummary(final HashMap<String, Object> data,
final Collection<TpmPcrEvent> eventList) {
boolean crtm = false;
boolean bootManager = false;
boolean osLoader = false;
boolean osKernel = false;
boolean acpiTables = false;
boolean smbiosTables = false;
boolean gptTable = false;
boolean bootOrder = false;
boolean defaultBootDevice = false;
boolean secureBoot = false;
boolean pk = false;
boolean kek = false;
boolean sigDb = false;
boolean forbiddenDbx = false;
String contentStr;
for (TpmPcrEvent tpe : eventList) {
contentStr = tpe.getEventContentStr();
// check for specific events
if (contentStr.contains("CRTM")) {
crtm = true;
} else if (contentStr.contains("shimx64.efi")
|| contentStr.contains("bootmgfw.efi")) {
bootManager = true;
} else if (contentStr.contains("grubx64.efi")
|| contentStr.contains("winload.efi")) {
osLoader = true;
} else if (contentStr.contains("vmlinuz")
|| contentStr.contains("ntoskrnl.exe")) {
osKernel = true;
} else if (contentStr.contains("ACPI")) {
acpiTables = true;
} else if (contentStr.contains("SMBIOS")) {
smbiosTables = true;
} else if (contentStr.contains("GPT")) {
gptTable = true;
} else if (contentStr.contains("BootOrder")) {
bootOrder = true;
} else if (contentStr.contains("Boot0000")) {
defaultBootDevice = true;
} else if (contentStr.contains("variable named PK")) {
pk = true;
} else if (contentStr.contains("variable named KEK")) {
kek = true;
} else if (contentStr.contains("variable named db")) {
if (contentStr.contains("dbx")) {
forbiddenDbx = true;
} else {
sigDb = true;
}
} else if (contentStr.contains("Secure Boot is enabled")) {
secureBoot = true;
}
}
data.put("crtm", crtm);
data.put("bootManager", bootManager);
data.put("osLoader", osLoader);
data.put("osKernel", osKernel);
data.put("acpiTables", acpiTables);
data.put("smbiosTables", smbiosTables);
data.put("gptTable", gptTable);
data.put("bootOrder", bootOrder);
data.put("defaultBootDevice", defaultBootDevice);
data.put("secureBoot", secureBoot);
data.put("pk", pk);
data.put("kek", kek);
data.put("sigDb", sigDb);
data.put("forbiddenDbx", forbiddenDbx);
}
/**
* 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 referenceManifestManager the reference manifest 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.
* @throws CertificateException if a certificate doesn't parse.
*/
private static HashMap<String, Object> getMeasurementsRimInfo(
final EventLogMeasurements measurements,
final ReferenceManifestManager referenceManifestManager,
final ReferenceDigestManager referenceDigestManager,
final ReferenceEventManager referenceEventManager)
throws IOException, CertificateException, NoSuchAlgorithmException {
HashMap<String, Object> data = new HashMap<>();
LinkedList<TpmPcrEvent> livelogEvents = new LinkedList<>();
BaseReferenceManifest base = null;
List<SupportReferenceManifest> supports = new ArrayList<>();
SupportReferenceManifest baseSupport = null;
data.put("supportFilename", "Blank");
data.put("supportId", "");
data.put("associatedRim", "");
data.put("rimType", measurements.getRimType());
data.put("hostName", measurements.getDeviceName());
data.put("validationResult", measurements.getOverallValidationResult());
data.put("swidBase", true);
List<ReferenceDigestValue> eventValues = new ArrayList<>();
if (measurements.getDeviceName() != null) {
supports.addAll(SupportReferenceManifest
.select(referenceManifestManager)
.byDeviceName(measurements
.getDeviceName()).getRIMs());
for (SupportReferenceManifest support : supports) {
if (support.isBaseSupport()) {
baseSupport = support;
}
}
if (baseSupport != null) {
data.put("supportFilename", baseSupport.getFileName());
data.put("supportId", baseSupport.getId());
base = BaseReferenceManifest
.select(referenceManifestManager)
.byEntityId(baseSupport.getAssociatedRim())
.getRIM();
data.put("tagId", baseSupport.getTagId());
if (base != null) {
data.put("associatedRim", base.getId());
}
eventValues.addAll(referenceEventManager.getValuesByRimId(base));
}
}
TCGEventLog measurementLog = new TCGEventLog(measurements.getRimBytes());
Map<String, ReferenceDigestValue> eventValueMap = new HashMap<>();
for (ReferenceDigestValue rdv : eventValues) {
eventValueMap.put(rdv.getDigestValue(), rdv);
}
for (TpmPcrEvent measurementEvent : measurementLog.getEventList()) {
if (!eventValueMap.containsKey(measurementEvent.getEventDigestStr())) {
livelogEvents.add(measurementEvent);
}
}
if (!supports.isEmpty()) {
Map<String, List<TpmPcrEvent>> baselineLogEvents = new HashMap<>();
List<TpmPcrEvent> matchedEvents = null;
List<TpmPcrEvent> combinedBaselines = new LinkedList<>();
for (SupportReferenceManifest support : supports) {
combinedBaselines.addAll(support.getEventLog());
}
String bootVariable;
String variablePrefix = "Variable Name:";
String variableSuffix = "UEFI_GUID";
for (TpmPcrEvent tpe : livelogEvents) {
matchedEvents = new ArrayList<>();
for (TpmPcrEvent tpmPcrEvent : combinedBaselines) {
if (tpmPcrEvent.getEventType() == tpe.getEventType()) {
if (tpe.getEventContentStr().contains(variablePrefix)) {
bootVariable = tpe.getEventContentStr().substring((
tpe.getEventContentStr().indexOf(variablePrefix)
+ variablePrefix.length()),
tpe.getEventContentStr().indexOf(variableSuffix));
if (tpmPcrEvent.getEventContentStr().contains(bootVariable)) {
matchedEvents.add(tpmPcrEvent);
}
} else {
matchedEvents.add(tpmPcrEvent);
}
}
}
baselineLogEvents.put(tpe.getEventDigestStr(), matchedEvents);
}
data.put("eventTypeMap", baselineLogEvents);
}
TCGEventLog logProcessor = new TCGEventLog(measurements.getRimBytes());
data.put("livelogEvents", livelogEvents);
data.put("events", logProcessor.getEventList());
getEventSummary(data, logProcessor.getEventList());
return data;
}
}

View File

@ -1,685 +0,0 @@
package hirs.attestationca.portal.page.controllers;
import hirs.FilteredRecordsList;
import hirs.attestationca.portal.datatables.DataTableInput;
import hirs.attestationca.portal.datatables.DataTableResponse;
import hirs.attestationca.portal.datatables.OrderedListQueryDataTableAdapter;
import hirs.attestationca.portal.page.Page;
import hirs.attestationca.portal.page.PageController;
import hirs.attestationca.portal.page.PageMessages;
import hirs.attestationca.portal.page.params.NoPageParams;
import hirs.data.persist.BaseReferenceManifest;
import hirs.data.persist.EventLogMeasurements;
import hirs.data.persist.ReferenceDigestValue;
import hirs.data.persist.ReferenceManifest;
import hirs.data.persist.SupportReferenceManifest;
import hirs.data.persist.SwidResource;
import hirs.data.persist.certificate.Certificate;
import hirs.persist.CriteriaModifier;
import hirs.persist.DBManagerException;
import hirs.persist.ReferenceEventManager;
import hirs.persist.ReferenceManifestManager;
import hirs.tpm.eventlog.TCGEventLog;
import hirs.tpm.eventlog.TpmPcrEvent;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.Hex;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.hibernate.Criteria;
import org.hibernate.criterion.Restrictions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.StreamUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import org.springframework.web.servlet.view.RedirectView;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URISyntaxException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
/**
* Controller for the Reference Manifest page.
*/
@Controller
@RequestMapping("/reference-manifests")
public class ReferenceManifestPageController
extends PageController<NoPageParams> {
private static final String BIOS_RELEASE_DATE_FORMAT = "yyyy-MM-dd";
private static final String LOG_FILE_PATTERN = "([^\\s]+(\\.(?i)(rimpcr|rimel|bin|log))$)";
private final BiosDateValidator biosValidator;
private final ReferenceManifestManager referenceManifestManager;
private final ReferenceEventManager referenceEventManager;
private static final Logger LOGGER
= LogManager.getLogger(ReferenceManifestPageController.class);
/**
* This class was created for the purposes of avoiding findbugs message: As
* the JavaDoc states, DateFormats are inherently unsafe for multi-threaded
* use. The detector has found a call to an instance of DateFormat that has
* been obtained via a static field. This looks suspicious.
*
* This class can have uses elsewhere but for now it will remain here.
*/
private static final class BiosDateValidator {
private final String dateFormat;
/**
* Default constructor that sets the format to parse against.
*
* @param dateFormat
*/
BiosDateValidator(final String dateFormat) {
this.dateFormat = dateFormat;
}
/**
* Validates a date by attempting to parse based on format provided.
*
* @param date string of the given date
* @return true if the format matches
*/
public boolean isValid(final String date) {
DateFormat validFormat = new SimpleDateFormat(this.dateFormat);
boolean result = true;
validFormat.setLenient(false);
try {
validFormat.parse(date);
} catch (ParseException pEx) {
result = false;
}
return result;
}
}
/**
* Constructor providing the Page's display and routing specification.
*
* @param referenceManifestManager the reference manifest manager
* @param referenceEventManager this is the reference event manager
*/
@Autowired
public ReferenceManifestPageController(
final ReferenceManifestManager referenceManifestManager,
final ReferenceEventManager referenceEventManager) {
super(Page.REFERENCE_MANIFESTS);
this.referenceManifestManager = referenceManifestManager;
this.referenceEventManager = referenceEventManager;
this.biosValidator = new BiosDateValidator(BIOS_RELEASE_DATE_FORMAT);
}
/**
* Returns the filePath for the view and the data model for the page.
*
* @param params The object to map url parameters into.
* @param model The data model for the request. Can contain data from
* redirect.
* @return the filePath for the view and data model for the page.
*/
@Override
public ModelAndView initPage(final NoPageParams params,
final Model model) {
return getBaseModelAndView();
}
/**
* Returns the list of RIMs using the data table input for paging, ordering,
* and filtering.
*
* @param input the data tables input
* @return the data tables response, including the result set and paging
* information
*/
@ResponseBody
@RequestMapping(value = "/list",
produces = MediaType.APPLICATION_JSON_VALUE,
method = RequestMethod.GET)
public DataTableResponse<ReferenceManifest> getTableData(
final DataTableInput input) {
LOGGER.debug("Handling request for summary list: " + input);
String orderColumnName = input.getOrderColumnName();
LOGGER.debug("Ordering on column: " + orderColumnName);
// check that the alert is not archived and that it is in the specified report
CriteriaModifier criteriaModifier = new CriteriaModifier() {
@Override
public void modify(final Criteria criteria) {
criteria.add(Restrictions.isNull(Certificate.ARCHIVE_FIELD));
}
};
FilteredRecordsList<ReferenceManifest> records
= OrderedListQueryDataTableAdapter.getOrderedList(
ReferenceManifest.class,
referenceManifestManager,
input, orderColumnName, criteriaModifier);
LOGGER.debug("Returning list of size: " + records.size());
return new DataTableResponse<>(records, input);
}
/**
* Upload and processes a reference manifest(s).
*
* @param files the files to process
* @param attr the redirection attributes
* @return the redirection view
* @throws URISyntaxException if malformed URI
* @throws Exception if malformed URI
*/
@RequestMapping(value = "/upload", method = RequestMethod.POST)
protected RedirectView upload(
@RequestParam("file") final MultipartFile[] files,
final RedirectAttributes attr) throws URISyntaxException, Exception {
Map<String, Object> model = new HashMap<>();
PageMessages messages = new PageMessages();
String fileName;
Pattern logPattern = Pattern.compile(LOG_FILE_PATTERN);
Matcher matcher;
boolean supportRIM = false;
List<BaseReferenceManifest> baseRims = new ArrayList<>();
List<SupportReferenceManifest> supportRims = new ArrayList<>();
// loop through the files
for (MultipartFile file : files) {
fileName = file.getOriginalFilename();
matcher = logPattern.matcher(fileName);
supportRIM = matcher.matches();
//Parse reference manifests
parseRIM(file, supportRIM, messages, baseRims, supportRims);
}
baseRims.stream().forEach((rim) -> {
LOGGER.info(String.format("Storing swidtag %s", rim.getFileName()));
storeManifest(messages, rim, false);
});
supportRims.stream().forEach((rim) -> {
LOGGER.info(String.format("Storing event log %s", rim.getFileName()));
storeManifest(messages, rim, true);
});
// Prep a map to associated the swidtag payload hash to the swidtag.
// pass it in to update support rims that either were uploaded
// or already exist
// create a map of the supports rims in case an uploaded swidtag
// isn't one to one with the uploaded support rims.
Map<String, SupportReferenceManifest> updatedSupportRims
= updateSupportRimInfo(generatePayloadHashMap(baseRims));
// look for missing uploaded support rims
for (SupportReferenceManifest support : supportRims) {
if (!updatedSupportRims.containsKey(support.getHexDecHash())) {
// Make sure we are getting the db version of the file
updatedSupportRims.put(support.getHexDecHash(),
SupportReferenceManifest
.select(referenceManifestManager)
.byHexDecHash(support.getHexDecHash())
.getRIM());
}
}
// pass in the updated support rims
// and either update or add the events
processTpmEvents(new ArrayList<SupportReferenceManifest>(updatedSupportRims.values()));
//Add messages to the model
model.put(MESSAGES_ATTRIBUTE, messages);
return redirectTo(Page.REFERENCE_MANIFESTS,
new NoPageParams(), model, attr);
}
/**
* Archives (soft delete) the Reference Integrity Manifest entry.
*
* @param id the UUID of the rim to delete
* @param attr RedirectAttributes used to forward data back to the original
* page.
* @return redirect to this page
* @throws URISyntaxException if malformed URI
*/
@RequestMapping(value = "/delete", method = RequestMethod.POST)
public RedirectView delete(@RequestParam final String id,
final RedirectAttributes attr) throws URISyntaxException {
LOGGER.info("Handling request to delete " + id);
Map<String, Object> model = new HashMap<>();
PageMessages messages = new PageMessages();
try {
ReferenceManifest referenceManifest = getRimFromDb(id);
if (referenceManifest == null) {
String notFoundMessage = "Unable to locate RIM with ID: " + id;
messages.addError(notFoundMessage);
LOGGER.warn(notFoundMessage);
} else {
referenceManifest.archive();
referenceManifestManager.update(referenceManifest);
String deleteCompletedMessage = "RIM successfully deleted";
messages.addInfo(deleteCompletedMessage);
LOGGER.info(deleteCompletedMessage);
// if support rim, update associated events
if (referenceManifest instanceof SupportReferenceManifest) {
List<ReferenceDigestValue> rdvs = referenceEventManager
.getValuesByRimId(referenceManifest);
for (ReferenceDigestValue rdv : rdvs) {
rdv.archive("Support RIM was deleted");
referenceEventManager.updateEvent(rdv);
}
}
}
} catch (IllegalArgumentException ex) {
String uuidError = "Failed to parse ID from: " + id;
messages.addError(uuidError);
LOGGER.error(uuidError, ex);
} catch (DBManagerException ex) {
String dbError = "Failed to archive cert: " + id;
messages.addError(dbError);
LOGGER.error(dbError, ex);
}
model.put(MESSAGES_ATTRIBUTE, messages);
return redirectTo(Page.REFERENCE_MANIFESTS, new NoPageParams(), model, attr);
}
/**
* Handles request to download the rim by writing it to the response stream
* for download.
*
* @param id the UUID of the rim to download
* @param response the response object (needed to update the header with the
* file name)
* @throws java.io.IOException when writing to response output stream
*/
@RequestMapping(value = "/download", method = RequestMethod.GET)
public void download(@RequestParam final String id,
final HttpServletResponse response)
throws IOException {
LOGGER.info("Handling RIM request to download " + id);
try {
ReferenceManifest referenceManifest = getRimFromDb(id);
if (referenceManifest == null) {
String notFoundMessage = "Unable to locate RIM with ID: " + id;
LOGGER.warn(notFoundMessage);
// send a 404 error when invalid Reference Manifest
response.sendError(HttpServletResponse.SC_NOT_FOUND);
} else {
StringBuilder fileName = new StringBuilder("filename=\"");
fileName.append(referenceManifest.getFileName());
// Set filename for download.
response.setHeader("Content-Disposition", "attachment;" + fileName);
response.setContentType("application/octet-stream");
// write cert to output stream
response.getOutputStream().write(referenceManifest.getRimBytes());
}
} catch (IllegalArgumentException ex) {
String uuidError = "Failed to parse ID from: " + id;
LOGGER.error(uuidError, ex);
// send a 404 error when invalid certificate
response.sendError(HttpServletResponse.SC_NOT_FOUND);
}
}
/**
* Handles request to download bulk of RIMs by writing it to the response stream
* for download in bulk.
*
* @param response the response object (needed to update the header with the
* file name)
* @throws java.io.IOException when writing to response output stream
*/
@RequestMapping(value = "/bulk", method = RequestMethod.GET)
public void bulk(final HttpServletResponse response)
throws IOException {
LOGGER.info("Handling request to download all Reference Integrity Manifests");
String fileName = "rims.zip";
String zipFileName;
// Set filename for download.
response.setHeader("Content-Disposition", "attachment; filename=" + fileName);
response.setContentType("application/zip");
List<ReferenceManifest> referenceManifestList = new LinkedList<>();
referenceManifestList.addAll(BaseReferenceManifest
.select(referenceManifestManager).getRIMs());
referenceManifestList.addAll(SupportReferenceManifest
.select(referenceManifestManager).getRIMs());
try (ZipOutputStream zipOut = new ZipOutputStream(response.getOutputStream())) {
// get all files
for (ReferenceManifest rim : referenceManifestList) {
if (rim.getFileName().isEmpty()) {
zipFileName = "";
} else {
// configure the zip entry, the properties of the 'file'
zipFileName = rim.getFileName();
}
ZipEntry zipEntry = new ZipEntry(zipFileName);
zipEntry.setSize((long) rim.getRimBytes().length * Byte.SIZE);
zipEntry.setTime(System.currentTimeMillis());
zipOut.putNextEntry(zipEntry);
// the content of the resource
StreamUtils.copy(rim.getRimBytes(), zipOut);
zipOut.closeEntry();
}
zipOut.finish();
// write cert to output stream
} catch (IllegalArgumentException ex) {
String uuidError = "Failed to parse ID from: ";
LOGGER.error(uuidError, ex);
// send a 404 error when invalid certificate
response.sendError(HttpServletResponse.SC_NOT_FOUND);
}
}
/**
* This method takes the parameter and looks for this information in the
* Database.
*
* @param id of the RIM
* @return the associated RIM from the DB
* @throws IllegalArgumentException
*/
private ReferenceManifest getRimFromDb(final String id) throws IllegalArgumentException {
UUID uuid = UUID.fromString(id);
ReferenceManifest rim = BaseReferenceManifest.select(referenceManifestManager)
.byEntityId(uuid).getRIM();
if (rim == null) {
rim = SupportReferenceManifest.select(referenceManifestManager)
.byEntityId(uuid).getRIM();
}
if (rim == null) {
rim = EventLogMeasurements.select(referenceManifestManager)
.byEntityId(uuid).getRIM();
}
return rim;
}
/**
* Takes the rim files provided and returns a {@link ReferenceManifest}
* object.
*
* @param file the provide user file via browser.
* @param supportRIM matcher result
* @param messages the object that handles displaying information to the
* user.
* @param baseRims object to store multiple files
* @param supportRims object to store multiple files
* @return a single or collection of reference manifest files.
*/
private void parseRIM(
final MultipartFile file, final boolean supportRIM,
final PageMessages messages, final List<BaseReferenceManifest> baseRims,
final List<SupportReferenceManifest> supportRims) {
byte[] fileBytes = new byte[0];
String fileName = file.getOriginalFilename();
// build the manifest from the uploaded bytes
try {
fileBytes = file.getBytes();
} catch (IOException e) {
final String failMessage
= String.format("Failed to read uploaded file (%s): ", fileName);
LOGGER.error(failMessage, e);
messages.addError(failMessage + e.getMessage());
}
try {
if (supportRIM) {
supportRims.add(new SupportReferenceManifest(fileName, fileBytes));
} else {
baseRims.add(new BaseReferenceManifest(fileName, fileBytes));
}
} catch (IOException ioEx) {
final String failMessage
= String.format("Failed to parse uploaded file (%s): ", fileName);
LOGGER.error(failMessage, ioEx);
messages.addError(failMessage + ioEx.getMessage());
}
}
/**
* Stores the {@link ReferenceManifest} objects.
*
* @param messages message object for user display of statuses
* @param referenceManifest the object to store
* @param supportRim boolean flag indicating if this is a support RIM
* process.
*/
private void storeManifest(
final PageMessages messages,
final ReferenceManifest referenceManifest,
final boolean supportRim) {
ReferenceManifest existingManifest = null;
String fileName = referenceManifest.getFileName();
MessageDigest digest = null;
String rimHash = "";
try {
digest = MessageDigest.getInstance("SHA-256");
} catch (NoSuchAlgorithmException noSaEx) {
LOGGER.error(noSaEx);
}
// look for existing manifest in the database
try {
if (supportRim) {
if (digest != null) {
rimHash = Hex.encodeHexString(
digest.digest(referenceManifest.getRimBytes()));
}
existingManifest = SupportReferenceManifest
.select(referenceManifestManager)
.byHexDecHash(rimHash)
.includeArchived()
.getRIM();
} else {
if (digest != null) {
rimHash = Base64.encodeBase64String(
digest.digest(referenceManifest.getRimBytes()));
}
existingManifest = BaseReferenceManifest
.select(referenceManifestManager).byBase64Hash(rimHash)
.includeArchived()
.getRIM();
}
} catch (DBManagerException e) {
final String failMessage = String.format("Querying for existing certificate "
+ "failed (%s): ", fileName);
messages.addError(failMessage + e.getMessage());
LOGGER.error(failMessage, e);
}
try {
// save the new certificate if no match is found
if (existingManifest == null) {
referenceManifestManager.save(referenceManifest);
final String successMsg = String.format("RIM successfully uploaded (%s): ",
fileName);
messages.addSuccess(successMsg);
LOGGER.info(successMsg);
}
} catch (DBManagerException dbmEx) {
final String failMessage = String.format("Storing RIM failed (%s): ",
fileName);
messages.addError(failMessage + dbmEx.getMessage());
LOGGER.error(failMessage, dbmEx);
}
try {
// if an identical RIM is archived, update the existing RIM to
// unarchive it and change the creation date
if (existingManifest != null && existingManifest.isArchived()) {
existingManifest.restore();
existingManifest.resetCreateTime();
referenceManifestManager.update(existingManifest);
final String successMsg
= String.format("Pre-existing RIM found and unarchived (%s): ", fileName);
messages.addSuccess(successMsg);
LOGGER.info(successMsg);
}
} catch (DBManagerException dbmEx) {
final String failMessage = String.format("Found an identical pre-existing RIM in the "
+ "archive, but failed to unarchive it (%s): ", fileName);
messages.addError(failMessage + dbmEx.getMessage());
LOGGER.error(failMessage, dbmEx);
}
}
private Map<String, BaseReferenceManifest> generatePayloadHashMap(
final List<BaseReferenceManifest> uploadedBaseRims) {
BaseReferenceManifest dbBaseRim;
HashMap<String, BaseReferenceManifest> tempMap = new HashMap<>();
for (BaseReferenceManifest base : uploadedBaseRims) {
// this is done to make sure we have the version with the UUID
dbBaseRim = BaseReferenceManifest.select(referenceManifestManager)
.byBase64Hash(base.getBase64Hash()).getRIM();
if (dbBaseRim != null) {
for (SwidResource swid : dbBaseRim.parseResource()) {
tempMap.put(swid.getHashValue(), dbBaseRim);
}
}
}
return tempMap;
}
private Map<String, SupportReferenceManifest> updateSupportRimInfo(
final Map<String, BaseReferenceManifest> dbBaseRims) {
BaseReferenceManifest dbBaseRim;
SupportReferenceManifest supportRim;
Map<String, SupportReferenceManifest> updatedSupportRims = new HashMap<>();
List<String> hashValues = new LinkedList<>(dbBaseRims.keySet());
for (String supportHash : hashValues) {
supportRim = SupportReferenceManifest.select(referenceManifestManager)
.byHexDecHash(supportHash).getRIM();
// I have to assume the baseRim is from the database
// Updating the id values, manufacturer, model
if (supportRim != null && !supportRim.isUpdated()) {
dbBaseRim = dbBaseRims.get(supportHash);
supportRim.setSwidTagVersion(dbBaseRim.getSwidTagVersion());
supportRim.setPlatformManufacturer(dbBaseRim.getPlatformManufacturer());
supportRim.setPlatformModel(dbBaseRim.getPlatformModel());
supportRim.setTagId(dbBaseRim.getTagId());
supportRim.setAssociatedRim(dbBaseRim.getId());
supportRim.setUpdated(true);
referenceManifestManager.update(supportRim);
updatedSupportRims.put(supportHash, supportRim);
}
}
return updatedSupportRims;
}
/**
* If the support rim is a supplemental or base, this method looks for the
* original oem base rim to associate with each event.
* @param supportRim assumed db object
* @return reference to the base rim
*/
private ReferenceManifest findBaseRim(final SupportReferenceManifest supportRim) {
if (supportRim != null && (supportRim.getId() != null
&& !supportRim.getId().toString().equals(""))) {
Set<BaseReferenceManifest> baseRims = BaseReferenceManifest
.select(referenceManifestManager)
.byManufacturerModel(supportRim.getPlatformManufacturer(),
supportRim.getPlatformModel()).getRIMs();
for (BaseReferenceManifest base : baseRims) {
if (base.isBase()) {
// there should be only one
return base;
}
}
}
return null;
}
private void processTpmEvents(final List<SupportReferenceManifest> dbSupportRims) {
boolean updated = true;
List<ReferenceDigestValue> tpmEvents;
TCGEventLog logProcessor = null;
ReferenceManifest baseRim;
for (SupportReferenceManifest dbSupport : dbSupportRims) {
// So first we'll have to pull values based on support rim
// get by support rim id NEXT
if (dbSupport.getPlatformManufacturer() != null) {
tpmEvents = referenceEventManager.getValuesByRimId(dbSupport);
baseRim = findBaseRim(dbSupport);
if (tpmEvents.isEmpty()) {
ReferenceDigestValue rdv;
try {
logProcessor = new TCGEventLog(dbSupport.getRimBytes());
for (TpmPcrEvent tpe : logProcessor.getEventList()) {
rdv = new ReferenceDigestValue(baseRim.getId(),
dbSupport.getId(), dbSupport.getPlatformManufacturer(),
dbSupport.getPlatformModel(), tpe.getPcrIndex(),
tpe.getEventDigestStr(), tpe.getEventTypeStr(),
false, false, updated, tpe.getEventContent());
this.referenceEventManager.saveValue(rdv);
}
} catch (CertificateException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
} else {
for (ReferenceDigestValue rdv : tpmEvents) {
if (!rdv.isUpdated()) {
rdv.updateInfo(dbSupport, baseRim.getId());
this.referenceEventManager.updateEvent(rdv);
}
}
}
}
}
}
}

View File

@ -1,186 +0,0 @@
package hirs.attestationca.portal.page.controllers;
import hirs.FilteredRecordsList;
import hirs.attestationca.portal.datatables.DataTableInput;
import hirs.attestationca.portal.datatables.DataTableResponse;
import hirs.attestationca.portal.datatables.OrderedListQueryDataTableAdapter;
import hirs.attestationca.portal.page.Page;
import hirs.attestationca.portal.page.PageController;
import hirs.attestationca.portal.page.params.NoPageParams;
import hirs.data.persist.ReferenceDigestValue;
import hirs.data.persist.SupportReferenceManifest;
import hirs.data.persist.certificate.Certificate;
import hirs.persist.CriteriaModifier;
import hirs.persist.DBManagerException;
import hirs.persist.DBReferenceDigestManager;
import hirs.persist.DBReferenceEventManager;
import hirs.persist.DBReferenceManifestManager;
import hirs.persist.ReferenceDigestManager;
import hirs.persist.ReferenceEventManager;
import hirs.persist.ReferenceManifestManager;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.hibernate.Criteria;
import org.hibernate.criterion.Restrictions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
/**
* Controller for the TPM Events page.
*/
@Controller
@RequestMapping("/rim-database")
public class RimDatabasePageController
extends PageController<NoPageParams> {
private static final String BIOS_RELEASE_DATE_FORMAT = "yyyy-MM-dd";
private final BiosDateValidator biosValidator;
private final ReferenceManifestManager referenceManifestManager;
private final ReferenceDigestManager referenceDigestManager;
private final ReferenceEventManager referenceEventManager;
private static final Logger LOGGER
= LogManager.getLogger(RimDatabasePageController.class);
/**
* This class was created for the purposes of avoiding findbugs message: As
* the JavaDoc states, DateFormats are inherently unsafe for multi-threaded
* use. The detector has found a call to an instance of DateFormat that has
* been obtained via a static field. This looks suspicious.
* <p>
* This class can have uses elsewhere but for now it will remain here.
*/
private static final class BiosDateValidator {
private final String dateFormat;
/**
* Default constructor that sets the format to parse against.
*
* @param dateFormat
*/
BiosDateValidator(final String dateFormat) {
this.dateFormat = dateFormat;
}
/**
* Validates a date by attempting to parse based on format provided.
*
* @param date string of the given date
* @return true if the format matches
*/
public boolean isValid(final String date) {
DateFormat validFormat = new SimpleDateFormat(this.dateFormat);
boolean result = true;
validFormat.setLenient(false);
try {
validFormat.parse(date);
} catch (ParseException pEx) {
result = false;
}
return result;
}
}
/**
* Constructor providing the Page's display and routing specification.
*
* @param referenceManifestManager the ReferenceManifestManager object
* @param referenceDigestManager the ReferenceDigestManager object
* @param referenceEventManager the referenceEventManager object
*/
@Autowired
public RimDatabasePageController(
final DBReferenceManifestManager referenceManifestManager,
final DBReferenceDigestManager referenceDigestManager,
final DBReferenceEventManager referenceEventManager) {
super(Page.RIM_DATABASE);
this.referenceManifestManager = referenceManifestManager;
this.referenceDigestManager = referenceDigestManager;
this.referenceEventManager = referenceEventManager;
this.biosValidator = new BiosDateValidator(BIOS_RELEASE_DATE_FORMAT);
}
/**
* Returns the filePath for the view and the data model for the page.
*
* @param params The object to map url parameters into.
* @param model The data model for the request. Can contain data from
* redirect.
* @return the filePath for the view and data model for the page.
*/
@Override
public ModelAndView initPage(final NoPageParams params,
final Model model) {
return getBaseModelAndView();
}
/**
* Returns the list of TPM Events using the data table input for paging, ordering,
* and filtering.
*
* @param input the data tables input
* @return the data tables response, including the result set and paging
* information
*/
@ResponseBody
@RequestMapping(value = "/list",
produces = MediaType.APPLICATION_JSON_VALUE,
method = RequestMethod.GET)
public DataTableResponse<ReferenceDigestValue> getTableData(
final DataTableInput input) {
LOGGER.info("Handling request for summary list: " + input);
String orderColumnName = input.getOrderColumnName();
LOGGER.info("Ordering on column: " + orderColumnName);
// check that the alert is not archived and that it is in the specified report
CriteriaModifier criteriaModifier = new CriteriaModifier() {
@Override
public void modify(final Criteria criteria) {
criteria.add(Restrictions.isNull(Certificate.ARCHIVE_FIELD));
}
};
LOGGER.info("Querying with the following datatableinput: " + input.toString());
FilteredRecordsList<ReferenceDigestValue> referenceDigestValues =
OrderedListQueryDataTableAdapter.getOrderedList(
ReferenceDigestValue.class,
referenceEventManager,
input, orderColumnName, criteriaModifier);
SupportReferenceManifest support;
for (ReferenceDigestValue rdv : referenceDigestValues) {
// We are updating the base rim ID field if necessary and
if (rdv.getBaseRimId() == null) {
support = SupportReferenceManifest.select(referenceManifestManager)
.byEntityId(rdv.getSupportRimId()).getRIM();
if (support != null) {
rdv.setBaseRimId(support.getAssociatedRim());
try {
referenceEventManager.updateEvent(rdv);
} catch (DBManagerException e) {
LOGGER.error("Failed to update TPM Event with Base RIM ID");
LOGGER.error(rdv);
}
}
}
}
return new DataTableResponse<>(referenceDigestValues, input);
}
}

View File

@ -1,446 +0,0 @@
package hirs.attestationca.portal.page.controllers;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import hirs.FilteredRecordsList;
import hirs.attestationca.portal.datatables.DataTableInput;
import hirs.attestationca.portal.datatables.DataTableResponse;
import hirs.attestationca.portal.datatables.OrderedListQueryDataTableAdapter;
import hirs.attestationca.portal.page.PageController;
import hirs.attestationca.portal.page.params.NoPageParams;
import hirs.data.persist.SupplyChainValidationSummary;
import hirs.data.persist.certificate.Certificate;
import hirs.data.persist.certificate.PlatformCredential;
import hirs.data.persist.certificate.attributes.ComponentIdentifier;
import hirs.data.persist.certificate.attributes.V2.ComponentIdentifierV2;
import hirs.persist.CertificateManager;
import hirs.persist.CriteriaModifier;
import hirs.persist.CrudManager;
import hirs.persist.DeviceManager;
import org.apache.logging.log4j.Logger;
import org.hibernate.Criteria;
import org.hibernate.criterion.Restrictions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.nio.charset.StandardCharsets;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import static hirs.attestationca.portal.page.Page.VALIDATION_REPORTS;
import static org.apache.logging.log4j.LogManager.getLogger;
/**
* Controller for the Validation Reports page.
*/
@Controller
@RequestMapping("/validation-reports")
public class ValidationReportsPageController extends PageController<NoPageParams> {
private final CrudManager<SupplyChainValidationSummary> supplyChainValidatorSummaryManager;
private final CertificateManager certificateManager;
private final DeviceManager deviceManager;
private static String systemColumnHeaders = "Verified Manufacturer,"
+ "Model,SN,Verification Date,Device Status";
private static String componentColumnHeaders = "Component name,Component manufacturer,"
+ "Component model,Component SN,Issuer,Component status";
private static final String DEFAULT_COMPANY = "AllDevices";
private static final String UNDEFINED = "undefined";
private static final String TRUE = "true";
private static final Logger LOGGER = getLogger(ValidationReportsPageController.class);
/**
* Constructor providing the Page's display and routing specification.
* @param supplyChainValidatorSummaryManager the manager
* @param certificateManager the certificate manager
* @param deviceManager the device manager
*/
@Autowired
public ValidationReportsPageController(
final CrudManager<SupplyChainValidationSummary> supplyChainValidatorSummaryManager,
final CertificateManager certificateManager,
final DeviceManager deviceManager) {
super(VALIDATION_REPORTS);
this.supplyChainValidatorSummaryManager = supplyChainValidatorSummaryManager;
this.certificateManager = certificateManager;
this.deviceManager = deviceManager;
}
/**
* Returns the path for the view and the data model for the page.
*
* @param params The object to map url parameters into.
* @param model The data model for the request. Can contain data from redirect.
* @return the path for the view and data model for the page.
*/
@Override
@RequestMapping
public ModelAndView initPage(final NoPageParams params, final Model model) {
return getBaseModelAndView();
}
/**
* Gets the list of validation summaries per the data table input query.
* @param input the data table query.
* @return the data table response containing the supply chain summary records
*/
@ResponseBody
@RequestMapping(value = "list", produces = MediaType.APPLICATION_JSON_VALUE,
method = RequestMethod.GET)
public DataTableResponse<SupplyChainValidationSummary> getTableData(
final DataTableInput input) {
LOGGER.debug("Handling request for summary list: " + input);
// attempt to get the column property based on the order index.
String orderColumnName = input.getOrderColumnName();
LOGGER.debug("Ordering on column: " + orderColumnName);
// define an alias so the composite object, device, can be used by the
// datatables / query. This is necessary so the device.name property can
// be used.
CriteriaModifier criteriaModifier = new CriteriaModifier() {
@Override
public void modify(final Criteria criteria) {
criteria.add(Restrictions.isNull(Certificate.ARCHIVE_FIELD));
criteria.createAlias("device", "device");
}
};
FilteredRecordsList<SupplyChainValidationSummary> records =
OrderedListQueryDataTableAdapter.getOrderedList(
SupplyChainValidationSummary.class,
supplyChainValidatorSummaryManager, input, orderColumnName,
criteriaModifier);
return new DataTableResponse<>(records, input);
}
/**
* This method handles downloading a validation report.
* @param request object
* @param response object
* @throws IOException thrown by BufferedWriter object
*/
@SuppressWarnings({"checkstyle:magicnumber", "checkstyle:methodlength" })
@RequestMapping(value = "download", method = RequestMethod.POST)
public void download(final HttpServletRequest request,
final HttpServletResponse response) throws IOException {
LOGGER.info("Downloading validation report");
String company = "";
String contractNumber = "";
Pattern pattern = Pattern.compile("^\\w*$");
DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("uuuu-MM-dd");
DateTimeFormatter dateTimeFormat = DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss");
LocalDate startDate = null;
LocalDate endDate = null;
ArrayList<LocalDate> createTimes = new ArrayList<LocalDate>();
String[] deviceNames = new String[]{};
String columnHeaders = "";
boolean systemOnly = false;
boolean componentOnly = false;
String filterManufacturer = "";
String filterSerial = "";
boolean jsonVersion = false;
Enumeration parameters = request.getParameterNames();
while (parameters.hasMoreElements()) {
String parameter = (String) parameters.nextElement();
String parameterValue = request.getParameter(parameter);
LOGGER.info(parameter + ": " + parameterValue);
switch (parameter) {
case "company":
Matcher companyMatcher = pattern.matcher(parameterValue);
if (companyMatcher.matches()) {
company = parameterValue;
} else {
company = DEFAULT_COMPANY;
}
break;
case "contract":
Matcher contractMatcher = pattern.matcher(parameterValue);
if (contractMatcher.matches()) {
contractNumber = parameterValue;
} else {
contractNumber = "none";
}
break;
case "dateStart":
if (parameterValue != null && !parameterValue.isEmpty()) {
startDate = LocalDate.parse(parameterValue, dateFormat);
} else {
startDate = LocalDate.ofEpochDay(0);
}
break;
case "dateEnd":
if (parameterValue != null && !parameterValue.isEmpty()) {
endDate = LocalDate.parse(parameterValue, dateFormat);
} else {
endDate = LocalDate.now();
}
break;
case "createTimes":
if (!parameterValue.equals(UNDEFINED)
&& !parameterValue.isEmpty()) {
String[] timestamps = parameterValue.split(",");
for (String timestamp : timestamps) {
createTimes.add(LocalDateTime.parse(timestamp,
dateTimeFormat).toLocalDate());
}
}
break;
case "deviceNames":
if (!parameterValue.equals(UNDEFINED)
&& !parameterValue.isEmpty()) {
deviceNames = parameterValue.split(",");
}
break;
case "system":
if (parameterValue.equals(TRUE)) {
systemOnly = true;
if (!columnHeaders.isEmpty()) {
columnHeaders = "," + columnHeaders;
}
columnHeaders = systemColumnHeaders + columnHeaders;
}
break;
case "component":
if (parameterValue.equals(TRUE)) {
componentOnly = true;
if (!columnHeaders.isEmpty()) {
columnHeaders += ",";
}
columnHeaders += componentColumnHeaders;
}
break;
case "manufacturer":
if (parameterValue != null && !parameterValue.isEmpty()) {
filterManufacturer = parameterValue;
}
break;
case "serial":
if (parameterValue != null && !parameterValue.isEmpty()) {
filterSerial = parameterValue;
}
break;
case "json":
response.setHeader("Content-Type", "application/json");
jsonVersion = true;
break;
default:
}
}
if (!jsonVersion) {
response.setHeader("Content-Type", "text/csv");
response.setHeader("Content-Disposition",
"attachment;filename=validation_report.csv");
}
BufferedWriter bufferedWriter = new BufferedWriter(
new OutputStreamWriter(response.getOutputStream(), StandardCharsets.UTF_8));
StringBuilder reportData = new StringBuilder();
JsonArray jsonReportData = new JsonArray();
for (int i = 0; i < deviceNames.length; i++) {
if ((createTimes.get(i).isAfter(startDate) || createTimes.get(i).isEqual(startDate))
&& (createTimes.get(i).isBefore(endDate)
|| createTimes.get(i).isEqual(endDate))) {
UUID deviceId = deviceManager.getDevice(deviceNames[i]).getId();
PlatformCredential pc = PlatformCredential.select(certificateManager)
.byDeviceId(deviceId).getCertificate();
if (jsonVersion) {
jsonReportData.add(assembleJsonContent(pc, parseComponents(pc),
company, contractNumber));
} else {
if (i == 0) {
bufferedWriter.append("Company: " + company + "\n");
bufferedWriter.append("Contract number: " + contractNumber + "\n");
}
if (systemOnly && componentOnly) {
systemOnly = false;
componentOnly = false;
}
if ((filterManufacturer.isEmpty() || filterManufacturer.equals(
pc.getManufacturer()))
&& (filterSerial.isEmpty() || filterSerial.equals(
pc.getPlatformSerial()))) {
if (!componentOnly) {
reportData.append(pc.getManufacturer() + ","
+ pc.getModel() + ","
+ pc.getPlatformSerial() + ","
+ LocalDateTime.now().toString() + ","
+ pc.getDevice().getSupplyChainStatus() + ",");
}
if (!systemOnly) {
ArrayList<ArrayList<String>> parsedComponents = parseComponents(pc);
for (ArrayList<String> component : parsedComponents) {
for (String data : component) {
reportData.append(data + ",");
}
reportData.deleteCharAt(reportData.length() - 1);
reportData.append(System.lineSeparator());
if (!componentOnly) {
reportData.append(",,,,,");
}
}
reportData = reportData.delete(
reportData.lastIndexOf(System.lineSeparator()) + 1,
reportData.length());
}
}
}
}
}
if (!jsonVersion) {
if (columnHeaders.isEmpty()) {
columnHeaders = systemColumnHeaders + "," + componentColumnHeaders;
}
bufferedWriter.append(columnHeaders + System.lineSeparator());
bufferedWriter.append(reportData.toString());
} else {
bufferedWriter.append(jsonReportData.toString());
}
bufferedWriter.flush();
}
/**
* This method builds a JSON object from the system and component data in a
* validation report.
* @param pc the platform credential used to validate.
* @param parsedComponents component data parsed from the platform credential.
* @param company company name.
* @param contractNumber contract number.
* @return the JSON object in String format.
*/
@SuppressWarnings({"checkstyle:magicnumber" })
private JsonObject assembleJsonContent(final PlatformCredential pc,
final ArrayList<ArrayList<String>> parsedComponents,
final String company,
final String contractNumber) {
JsonObject systemData = new JsonObject();
systemData.addProperty("Company", company);
systemData.addProperty("Contract number", contractNumber);
systemData.addProperty("Verified Manufacturer", pc.getManufacturer());
systemData.addProperty("Model", pc.getModel());
systemData.addProperty("SN", pc.getPlatformSerial());
systemData.addProperty("Verification Date", LocalDateTime.now().toString());
systemData.addProperty("Device Status", pc.getDevice().getSupplyChainStatus().toString());
JsonArray components = new JsonArray();
for (ArrayList<String> componentData : parsedComponents) {
JsonObject component = new JsonObject();
component.addProperty("Component name", componentData.get(0));
component.addProperty("Component manufacturer", componentData.get(1));
component.addProperty("Component model", componentData.get(2));
component.addProperty("Component SN", componentData.get(3));
component.addProperty("Issuer", componentData.get(4));
component.addProperty("Component status", componentData.get(5));
components.add(component);
}
systemData.add("Components", components);
return systemData;
}
/**
* This method parses the following ComponentIdentifier fields into an ArrayList of ArrayLists.
* - ComponentClass
* - Manufacturer
* - Model
* - Serial number
* - Pass/fail status (based on componentFailures string)
* @param pc the platform credential.
* @return the ArrayList of ArrayLists containing the parsed component data.
*/
private ArrayList<ArrayList<String>> parseComponents(final PlatformCredential pc) {
ArrayList<ArrayList<String>> parsedComponents = new ArrayList<ArrayList<String>>();
ArrayList<ArrayList<Object>> chainComponents = new ArrayList<>();
StringBuilder componentFailureString = new StringBuilder();
if (pc.getComponentIdentifiers() != null
&& pc.getComponentIdentifiers().size() > 0) {
componentFailureString.append(pc.getComponentFailures());
// get all the certificates associated with the platform serial
List<PlatformCredential> chainCertificates = PlatformCredential
.select(certificateManager)
.byBoardSerialNumber(pc.getPlatformSerial())
.getCertificates().stream().collect(Collectors.toList());
// combine all components in each certificate
for (ComponentIdentifier ci : pc.getComponentIdentifiers()) {
ArrayList<Object> issuerAndComponent = new ArrayList<Object>();
issuerAndComponent.add(pc.getIssuer());
issuerAndComponent.add(ci);
chainComponents.add(issuerAndComponent);
}
for (PlatformCredential cert : chainCertificates) {
componentFailureString.append(cert.getComponentFailures());
if (!cert.isBase()) {
for (ComponentIdentifier ci : cert.getComponentIdentifiers()) {
ArrayList<Object> issuerAndComponent = new ArrayList<Object>();
issuerAndComponent.add(cert.getIssuer());
issuerAndComponent.add(ci);
chainComponents.add(issuerAndComponent);
}
}
}
LOGGER.info("Component failures: " + componentFailureString.toString());
for (ArrayList<Object> issuerAndComponent : chainComponents) {
ArrayList<String> componentData = new ArrayList<String>();
String issuer = (String) issuerAndComponent.get(0);
issuer = issuer.replaceAll(",", " ");
ComponentIdentifier ci = (ComponentIdentifier) issuerAndComponent.get(1);
if (ci instanceof ComponentIdentifierV2) {
String componentClass =
((ComponentIdentifierV2) ci).getComponentClass().toString();
String[] splitStrings = componentClass.split("\r\n|\n|\r");
StringBuilder sb = new StringBuilder();
for (String s : splitStrings) {
sb.append(s);
sb.append(" ");
}
sb = sb.deleteCharAt(sb.length() - 1);
componentData.add(sb.toString());
} else {
componentData.add("Platform Component");
}
componentData.add(ci.getComponentManufacturer().getString());
componentData.add(ci.getComponentModel().getString());
componentData.add(ci.getComponentSerial().getString());
componentData.add(issuer);
//Failing components are identified by hashcode
if (componentFailureString.toString().contains(String.valueOf(ci.hashCode()))) {
componentData.add("Fail");
} else {
componentData.add("Pass");
}
parsedComponents.add(componentData);
LOGGER.info(String.join(",", componentData));
}
}
return parsedComponents;
}
}

View File

@ -1,4 +0,0 @@
/**
* This package contains PageController implementations.
*/
package hirs.attestationca.portal.page.controllers;

View File

@ -1,4 +0,0 @@
/**
* This package provides interfaces and superclasses for PageController and associated objects.
*/
package hirs.attestationca.portal.page;

View File

@ -1,99 +0,0 @@
package hirs.attestationca.portal.page.params;
import hirs.attestationca.portal.page.PageParams;
import java.util.LinkedHashMap;
/**
* URL parameters object for the CertificateDetails page and controller.
*/
public class CertificateDetailsPageParams implements PageParams {
private String id;
private String type;
/**
* Constructor to set all Certificate Details URL parameters.
*
* @param id the String parameter to set
* @param type the Integer parameter to set
*/
public CertificateDetailsPageParams(final String id, final String type) {
this.id = id;
this.type = type;
}
/**
* Constructor to set ID Certificate Details URL parameters.
*
* @param id the String parameter to set
*/
public CertificateDetailsPageParams(final String id) {
this.id = id;
}
/**
* Default constructor for Spring.
*/
public CertificateDetailsPageParams() {
id = null;
type = null;
}
/**
* Returns the String id parameter.
*
* @return the String id parameter.
*/
public String getId() {
return id;
}
/**
* Sets the String id parameter.
*
* @param id the String id parameter.
*/
public void setId(final String id) {
this.id = id;
}
/**
* Returns the String type parameter.
*
* @return the String type parameter.
*/
public String getType() {
return type;
}
/**
* Sets the String type parameter.
*
* @param type the String type parameter.
*/
public void setType(final String type) {
this.type = type;
}
/**
* Allows PageController to iterate over the url parameters.
*
* @return map containing the object's URL parameters.
*/
@Override
public LinkedHashMap<String, ?> asMap() {
LinkedHashMap<String, Object> map = new LinkedHashMap<>();
map.put("id", id);
map.put("type", type);
return map;
}
@Override
public String toString() {
return "CertificateDetailsPageParams{"
+ "id:' " + id + "',"
+ "type: " + type
+ "}";
}
}

View File

@ -1,67 +0,0 @@
package hirs.attestationca.portal.page.params;
import hirs.attestationca.portal.page.PageParams;
import java.util.LinkedHashMap;
/**
* URL parameters object for the Reference Manifest Details page and controller.
*/
public class ReferenceManifestDetailsPageParams implements PageParams {
private String id;
/**
* Constructor to set all RIM Details URL parameters.
*
* @param id the String parameter to set
*/
public ReferenceManifestDetailsPageParams(final String id) {
this.id = id;
}
/**
* Default constructor for Spring.
*
*/
public ReferenceManifestDetailsPageParams() {
this.id = null;
}
/**
* Returns the String id parameter.
*
* @return the String id parameter.
*/
public String getId() {
return id;
}
/**
* Sets the String id parameter.
*
* @param id the String id parameter.
*/
public void setId(final String id) {
this.id = id;
}
/**
* Allows PageController to iterate over the url parameters.
*
* @return map containing the object's URL parameters.
*/
@Override
public LinkedHashMap<String, ?> asMap() {
LinkedHashMap<String, Object> map = new LinkedHashMap<>();
map.put("id", id);
return map;
}
@Override
public String toString() {
return "ReferenceManifestDetailsPageParams{"
+ "id:' " + id
+ "}";
}
}

View File

@ -1,91 +0,0 @@
package hirs.attestationca.portal.page.params;
import hirs.attestationca.portal.page.PageParams;
import java.util.LinkedHashMap;
/**
* URL parameters object for the ReferenceManifest page and controller.
*/
public class ReferenceManifestPageParams implements PageParams {
private String id;
private String type;
/**
* Constructor to set all Reference Integrity Manifest URL parameters.
*
* @param id the String parameter to set
* @param type the Integer parameter to set
*/
public ReferenceManifestPageParams(final String id, final String type) {
this.id = id;
this.type = type;
}
/**
*Constructor to set all Reference Integrity Manifest URL parameters.
*
* @param id the String parameter to set
*/
public ReferenceManifestPageParams(final String id) {
this.id = id;
}
/**
* Default constructor for Spring.
*/
public ReferenceManifestPageParams() {
id = null;
type = null;
}
/**
* Returns the String id parameter.
*
* @return the String id parameter.
*/
public String getId() {
return id;
}
/**
* Sets the String id parameter.
*
* @param id the String id parameter.
*/
public void setId(final String id) {
this.id = id;
}
/**
* Returns the String type parameter.
*
* @return the String type parameter.
*/
public String getType() {
return type;
}
/**
* Sets the String type parameter.
*
* @param type the String type parameter.
*/
public void setType(final String type) {
this.type = type;
}
/**
* Allows PageController to iterate over the url parameters.
*
* @return map containing the object's URL parameters.
*/
@Override
public LinkedHashMap<String, ?> asMap() {
LinkedHashMap<String, Object> map = new LinkedHashMap<>();
map.put("id", id);
map.put("type", type);
return map;
}
}

View File

@ -1,4 +0,0 @@
/**
* This package contains url parameter classes for portal pages.
*/
package hirs.attestationca.portal.page.params;

View File

@ -1,125 +0,0 @@
package hirs.attestationca.portal.persistence;
import hirs.data.persist.SupplyChainValidationSummary;
import hirs.persist.AppraiserManager;
import hirs.persist.CrudManager;
import hirs.persist.DBAppraiserManager;
import hirs.persist.DBCertificateManager;
import hirs.persist.DBDeviceGroupManager;
import hirs.persist.DBDeviceManager;
import hirs.persist.DBManager;
import hirs.persist.DBPolicyManager;
import hirs.persist.DBReferenceDigestManager;
import hirs.persist.DBReferenceEventManager;
import hirs.persist.DBReferenceManifestManager;
import hirs.persist.DeviceGroupManager;
import hirs.persist.DeviceManager;
import hirs.persist.HibernateConfiguration;
import hirs.persist.PolicyManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.orm.hibernate4.LocalSessionFactoryBean;
/**
* Spring configuration class for persistence beans used by the Attestation CA Portal.
*/
@Configuration
@Import({ HibernateConfiguration.class })
public class PersistenceConfiguration {
@Autowired
private LocalSessionFactoryBean sessionFactory;
/**
* Creates a {@link PolicyManager} ready to use.
*
* @return {@link PolicyManager}
*/
@Bean
public PolicyManager policyManager() {
return new DBPolicyManager(sessionFactory.getObject());
}
/**
* Creates a {@link DeviceManager} ready to use.
*
* @return {@link DeviceManager}
*/
@Bean
public DeviceManager deviceManager() {
return new DBDeviceManager(sessionFactory.getObject());
}
/**
* Creates a {@link DBCertificateManager} ready to use.
*
* @return {@link DBCertificateManager}
*/
@Bean
public DBCertificateManager certificateManager() {
return new DBCertificateManager(sessionFactory.getObject());
}
/**
* Creates a {@link DBReferenceManifestManager} ready to use.
*
* @return {@link DBReferenceManifestManager}
*/
@Bean
public DBReferenceManifestManager referenceManifestManager() {
return new DBReferenceManifestManager(sessionFactory.getObject());
}
/**
* Creates a {@link DBReferenceDigestManager} ready to use.
*
* @return {@link DBReferenceDigestManager}
*/
@Bean
public DBReferenceDigestManager referenceDigestManager() {
return new DBReferenceDigestManager(sessionFactory.getObject());
}
/**
* Creates a {@link DBReferenceEventManager} ready to use.
*
* @return {@link DBReferenceEventManager}
*/
@Bean
public DBReferenceEventManager referenceEventManager() {
return new DBReferenceEventManager(sessionFactory.getObject());
}
/**
* Creates a {@link AppraiserManager} ready to use.
*
* @return {@link AppraiserManager}
*/
@Bean
public AppraiserManager appraiserManager() {
return new DBAppraiserManager(sessionFactory.getObject());
}
/**
* Creates a {@link DeviceGroupManager} ready to use.
*
* @return {@link DeviceGroupManager}
*/
@Bean
public DeviceGroupManager deviceGroupManager() {
return new DBDeviceGroupManager(sessionFactory.getObject());
}
/**
* Creates a {@link DBManager} for SupplyChainValidationSummary persistence, ready for use.
*
* @return {@link DBManager}
*/
@Bean
public CrudManager<SupplyChainValidationSummary> supplyChainValidationSummaryManager() {
return new DBManager<>(SupplyChainValidationSummary.class, sessionFactory.getObject());
}
}

View File

@ -1,4 +0,0 @@
/**
* Persistence related classes for Attestation CA Portal.
*/
package hirs.attestationca.portal.persistence;

View File

@ -0,0 +1,15 @@
package hirs.attestationca.portal.service;
public class DbServiceImpl {
/**
* The default maximum number of retries to attempt a database transaction.
*/
public static final int DEFAULT_MAX_RETRY_ATTEMPTS = 10;
/*
* The default number of milliseconds to wait before retrying a database transaction.
*/
private static final long DEFAULT_RETRY_WAIT_TIME_MS = 3000;
// structure for retrying methods in the database
// private RetryTemplate retryTemplate;
}

View File

@ -0,0 +1,4 @@
package hirs.attestationca.portal.service;
public interface DefaultService {
}

View File

@ -0,0 +1,46 @@
package hirs.attestationca.portal.service;
import hirs.attestationca.portal.entity.userdefined.Device;
import hirs.attestationca.portal.enums.AppraisalStatus;
import hirs.attestationca.portal.enums.HealthStatus;
import jakarta.persistence.EntityManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* https://github.com/darrachequesne/spring-data-jpa-datatables
*/
@Service
public class DeviceServiceImpl {
@Autowired(required = false)
private EntityManager entityManager;
private static List<Device> devices = new ArrayList<>(Arrays.asList(
new Device("Dell", HealthStatus.TRUSTED,
AppraisalStatus.Status.UNKNOWN,
Timestamp.valueOf(LocalDateTime.MAX), false, "testing", "resting"),
new Device("Intel", HealthStatus.UNTRUSTED,
AppraisalStatus.Status.FAIL,
Timestamp.valueOf(LocalDateTime.MIN), false, "testing", "resting"),
new Device("Cybex", HealthStatus.UNKNOWN,
AppraisalStatus.Status.PASS,
Timestamp.valueOf(LocalDateTime.now()), false, "testing", "resting")));
public List<Device> retrieveDevices() {
List<Device> devices = new ArrayList<Device>();
for (Device device : this.devices) {
devices.add(device);
}
return devices;
}
}

View File

@ -0,0 +1,46 @@
package hirs.attestationca.portal.service;
import hirs.attestationca.portal.entity.manager.SettingsRepository;
import hirs.attestationca.portal.entity.userdefined.SupplyChainSettings;
import jakarta.persistence.EntityManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.UUID;
@Service
public class SettingsServiceImpl {
@Autowired(required = false)
private EntityManager entityManager;
@Autowired
private SettingsRepository repository;
public SupplyChainSettings updateSettings(SupplyChainSettings settings, UUID uuid) {
if (repository.existsById(uuid)) {
// already in DB
}
return updateSettings(settings);
}
public SupplyChainSettings updateSettings(SupplyChainSettings settings) {
return repository.save(settings);
}
public void saveSettings(SupplyChainSettings settings) {
repository.save(settings);
}
public SupplyChainSettings getByName(String name) {
if (name == null) {
return null;
}
return repository.findByName(name);
}
// public Policy getDefaultPolicy(Appraiser appraiser) {
// return repository.findByAppraiser(appraiser);
// }
}

Some files were not shown because too many files have changed in this diff Show More