Merge remote-tracking branch 'origin/codebase-reset' into gradle-package

This commit is contained in:
root 2023-04-25 18:37:28 +00:00
commit 6763e4a87d
64 changed files with 11167 additions and 1675 deletions

View File

@ -0,0 +1,14 @@
package hirs.attestationca.persist;
import jakarta.persistence.criteria.CriteriaQuery;
/**
* Allows a user of the DBManager to modify the criteria object before processing.
*/
public interface CriteriaModifier<T> {
/**
* Allows a client to modify the criteria object by reference.
* @param criteria The hibernate criteria builder object
*/
void modify(CriteriaQuery<T> criteria);
}

View File

@ -0,0 +1,46 @@
package hirs.attestationca.persist;
/**
* This class represents an <code>Exception</code> generated by a
* <code>DBManageer</code>.
*/
public class DBManagerException extends RuntimeException {
private static final long serialVersionUID = 3081536085161873284L;
/**
* Creates a new <code>DBManagerException</code> that has the message
* <code>msg</code>.
*
* @param msg
* exception message
*/
public DBManagerException(final String msg) {
super(msg);
}
/**
* Creates a new <code>DBManagerException</code> that wraps the given
* <code>Throwable</code>.
*
* @param t
* root cause
*/
public DBManagerException(final Throwable t) {
super(t);
}
/**
* Creates a new <code>DBManagerException</code> that has the message
* <code>msg</code> and wraps the root cause.
*
* @param msg
* exception message
* @param t
* root cause
*/
public DBManagerException(final String msg, final Throwable t) {
super(msg, t);
}
}

View File

@ -0,0 +1,40 @@
package hirs.attestationca.persist;
/**
* This class represents an <code>Exception</code> generated by a
* <code>DBManageer</code>.
*/
public class DBServiceException extends RuntimeException {
private static final long serialVersionUID = 3081536085161873284L;
/**
* Creates a new <code>DBManagerException</code> that has the message
* <code>msg</code>.
*
* @param msg exception message
*/
public DBServiceException(final String msg) {
super(msg);
}
/**
* Creates a new <code>DBManagerException</code> that wraps the given
* <code>Throwable</code>.
*
* @param t root cause
*/
public DBServiceException(final Throwable t) {
super(t);
}
/**
* Creates a new <code>DBManagerException</code> that has the message
* <code>msg</code> and wraps the root cause.
*
* @param msg exception message
* @param t root cause
*/
DBServiceException(final String msg, final Throwable t) {
super(msg, t);
}
}

View File

@ -0,0 +1,21 @@
package hirs.attestationca.persist;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.ArrayList;
/**
* FilteredRecordsList is an object designed to hold the results from multiple
* queries necessary to populate the JQuery Datatables. The members include
* the total number of records in the entity, the number of records returned
* after filtering through the search bar, and the records themselves.
*
* @param <T> Class accepts generic for the list of data records.
*/
@Data
@EqualsAndHashCode(callSuper=false)
public class FilteredRecordsList<T> extends ArrayList<T> {
private long recordsTotal, recordsFiltered;
}

View File

@ -0,0 +1,65 @@
package hirs.attestationca.persist;
import java.util.Map;
/**
* Interface defining methods for getting ordered lists from a data source. Includes
* properties for sorting, paging, and searching.
* @param <T> the record type, T.
*/
public interface OrderedListQuerier<T> {
/**
* Returns a list of all <code>T</code>s that are ordered by a column and
* direction (ASC, DESC) that is provided by the user. This method helps
* support the server-side processing in the JQuery DataTables.
*
* @param clazz class type of <code>T</code>s to search for (may be null to
* use Class&lt;T&gt;)
* @param columnToOrder Column to be ordered
* @param ascending direction of sort
* @param firstResult starting point of first result in set
* @param maxResults total number we want returned for display in table
* @param search string of criteria to be matched to visible columns
* @param searchableColumns Map of String and boolean values with column
* headers and whether they should be searched. Boolean is true if field provides a
* typical String that can be searched by Hibernate without transformation.
* @return FilteredRecordsList object with query data
* @throws DBManagerException if unable to create the list
*/
FilteredRecordsList getOrderedList(
Class<? extends T> clazz, String columnToOrder,
boolean ascending, int firstResult,
int maxResults, String search,
Map<String, Boolean> searchableColumns)
throws DBManagerException;
/**
* Returns a list of all <code>T</code>s that are ordered by a column and
* direction (ASC, DESC) that is provided by the user. This method helps
* support the server-side processing in the JQuery DataTables. For entities that support
* soft-deletes, the returned list does not contain <code>T</code>s that have been soft-deleted.
*
* @param clazz class type of <code>T</code>s to search for (may be null to
* use Class&lt;T&gt;)
* @param columnToOrder Column to be ordered
* @param ascending direction of sort
* @param firstResult starting point of first result in set
* @param maxResults total number we want returned for display in table
* @param search string of criteria to be matched to visible columns
* @param searchableColumns Map of String and boolean values with column
* headers and whether they should be searched. Boolean is true if field provides a
* typical String that can be searched by Hibernate without transformation.
* @param criteriaModifier a way to modify the criteria used in the query
* @return FilteredRecordsList object with query data
* @throws DBManagerException if unable to create the list
*/
@SuppressWarnings("checkstyle:parameternumber")
FilteredRecordsList<T> getOrderedList(
Class<? extends T> clazz, String columnToOrder,
boolean ascending, int firstResult,
int maxResults, String search,
Map<String, Boolean> searchableColumns, CriteriaModifier<T> criteriaModifier)
throws DBManagerException;
}

View File

@ -2,15 +2,15 @@ package hirs.attestationca.persist.entity;
import jakarta.persistence.Column; import jakarta.persistence.Column;
import jakarta.persistence.GeneratedValue; import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id; import jakarta.persistence.Id;
import jakarta.persistence.MappedSuperclass; import jakarta.persistence.MappedSuperclass;
import lombok.EqualsAndHashCode;
import lombok.Getter; import lombok.Getter;
import lombok.ToString; import lombok.ToString;
import org.hibernate.annotations.ColumnDefault; import org.hibernate.annotations.ColumnDefault;
import org.hibernate.annotations.Generated; import org.hibernate.annotations.Generated;
import org.hibernate.annotations.GenerationTime; import org.hibernate.annotations.GenerationTime;
import org.hibernate.annotations.JdbcTypeCode; import org.hibernate.annotations.UuidGenerator;
import java.io.Serializable; import java.io.Serializable;
import java.util.Date; import java.util.Date;
@ -19,6 +19,7 @@ import java.util.UUID;
/** /**
* An abstract database entity. * An abstract database entity.
*/ */
@EqualsAndHashCode
@ToString @ToString
@MappedSuperclass @MappedSuperclass
public abstract class AbstractEntity implements Serializable { public abstract class AbstractEntity implements Serializable {
@ -31,8 +32,8 @@ public abstract class AbstractEntity implements Serializable {
@Id @Id
@Column(name = "id") @Column(name = "id")
@GeneratedValue(generator = "uuid2", strategy=GenerationType.AUTO) @UuidGenerator(style = UuidGenerator.Style.AUTO)
@JdbcTypeCode(java.sql.Types.VARCHAR) @GeneratedValue
@Getter @Getter
private UUID id; private UUID id;
@ -75,26 +76,4 @@ public abstract class AbstractEntity implements Serializable {
public void resetCreateTime() { public void resetCreateTime() {
createTime.setTime(new Date().getTime()); createTime.setTime(new Date().getTime());
} }
@Override
public int hashCode() {
if (id != null) {
return id.hashCode();
}
return super.hashCode();
}
@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (!(this.getClass().equals(obj.getClass()))) {
return false;
}
return this.hashCode() == obj.hashCode();
}
} }

View File

@ -4,6 +4,8 @@ import jakarta.persistence.Column;
import jakarta.persistence.MappedSuperclass; import jakarta.persistence.MappedSuperclass;
import lombok.Getter; import lombok.Getter;
import lombok.ToString; import lombok.ToString;
import org.hibernate.annotations.JdbcTypeCode;
import org.hibernate.type.SqlTypes;
import java.util.Date; import java.util.Date;
@ -23,6 +25,7 @@ public abstract class ArchivableEntity extends AbstractEntity {
@Column(name = "archived_time") @Column(name = "archived_time")
private Date archivedTime; private Date archivedTime;
@JdbcTypeCode(SqlTypes.LONGVARCHAR)
@Column(name = "archived_description") @Column(name = "archived_description")
private String archivedDescription; private String archivedDescription;

View File

@ -0,0 +1,11 @@
package hirs.attestationca.persist.entity.manager;
import hirs.attestationca.persist.entity.userdefined.rim.ReferenceDigestValue;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.UUID;
@Repository
public interface ReferenceDigestValueRepository extends JpaRepository<ReferenceDigestValue, UUID> {
}

View File

@ -0,0 +1,9 @@
package hirs.attestationca.persist.entity.manager;
import hirs.attestationca.persist.entity.userdefined.SupplyChainValidation;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.UUID;
public interface SupplyChainValidationRepository extends JpaRepository<SupplyChainValidation, UUID> {
}

View File

@ -9,8 +9,7 @@ import jakarta.persistence.Column;
import jakarta.persistence.Entity; import jakarta.persistence.Entity;
import jakarta.persistence.Transient; import jakarta.persistence.Transient;
import lombok.Getter; import lombok.Getter;
import org.apache.logging.log4j.LogManager; import lombok.extern.log4j.Log4j2;
import org.apache.logging.log4j.Logger;
import org.bouncycastle.asn1.ASN1BitString; import org.bouncycastle.asn1.ASN1BitString;
import org.bouncycastle.asn1.ASN1Encodable; import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.ASN1GeneralizedTime; import org.bouncycastle.asn1.ASN1GeneralizedTime;
@ -76,11 +75,10 @@ import java.util.Objects;
* It stores certain attributes separately from the serialized certificate to enable querying on * It stores certain attributes separately from the serialized certificate to enable querying on
* those attributes. * those attributes.
*/ */
@Log4j2
@Entity @Entity
public abstract class Certificate extends ArchivableEntity { public abstract class Certificate extends ArchivableEntity {
private static final Logger LOGGER = LogManager.getLogger(Certificate.class);
/** /**
* Holds the different certificate types. * Holds the different certificate types.
*/ */
@ -561,8 +559,8 @@ public abstract class Certificate extends ArchivableEntity {
try { try {
return getX509Certificate().getVersion() - 1; return getX509Certificate().getVersion() - 1;
} catch (IOException ex) { } catch (IOException ex) {
LOGGER.warn("X509 Credential Version not found."); log.warn("X509 Credential Version not found.");
LOGGER.error(ex); log.error(ex);
return Integer.MAX_VALUE; return Integer.MAX_VALUE;
} }
} }
@ -589,7 +587,7 @@ public abstract class Certificate extends ArchivableEntity {
isIssuer = ""; isIssuer = "";
} catch (CertificateException | NoSuchAlgorithmException | InvalidKeyException } catch (CertificateException | NoSuchAlgorithmException | InvalidKeyException
| NoSuchProviderException | SignatureException e) { | NoSuchProviderException | SignatureException e) {
LOGGER.error(e); log.error(e);
} }
break; break;
case ATTRIBUTE_CERTIFICATE: case ATTRIBUTE_CERTIFICATE:
@ -604,7 +602,7 @@ public abstract class Certificate extends ArchivableEntity {
} catch (NoSuchAlgorithmException } catch (NoSuchAlgorithmException
| InvalidKeyException | InvalidKeyException
| SignatureException sigEx) { | SignatureException sigEx) {
LOGGER.error(sigEx); log.error(sigEx);
} }
break; break;
default: default:
@ -769,7 +767,7 @@ public abstract class Certificate extends ArchivableEntity {
asn1InputStream = new ASN1InputStream(oct.getOctets()); asn1InputStream = new ASN1InputStream(oct.getOctets());
asn1Primitive = asn1InputStream.readObject(); asn1Primitive = asn1InputStream.readObject();
} catch (IOException ioEx) { } catch (IOException ioEx) {
LOGGER.error(ioEx); log.error(ioEx);
} finally { } finally {
if (asn1InputStream != null) { if (asn1InputStream != null) {
asn1InputStream.close(); asn1InputStream.close();
@ -794,7 +792,7 @@ public abstract class Certificate extends ArchivableEntity {
.getInstance(JcaX509ExtensionUtils.parseExtensionValue(authInfoAccess)))); .getInstance(JcaX509ExtensionUtils.parseExtensionValue(authInfoAccess))));
} }
} catch (IOException ioEx) { } catch (IOException ioEx) {
LOGGER.error(ioEx); log.error(ioEx);
} }
return sb.toString(); return sb.toString();
@ -993,7 +991,7 @@ public abstract class Certificate extends ArchivableEntity {
certificateHolder.getSubjectPublicKeyInfo().parsePublicKey().toASN1Primitive() certificateHolder.getSubjectPublicKeyInfo().parsePublicKey().toASN1Primitive()
); );
} catch (IOException e) { } catch (IOException e) {
LOGGER.info("No RSA Key Detected in certificate"); log.info("No RSA Key Detected in certificate");
return null; return null;
} }
} }

View File

@ -1,72 +0,0 @@
package hirs.attestationca.persist.entity.userdefined;
import hirs.attestationca.persist.entity.ArchivableEntity;
import jakarta.persistence.Access;
import jakarta.persistence.AccessType;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Table;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.hibernate.annotations.JdbcTypeCode;
import java.util.UUID;
/**
* This class represents that actual entry in the Support RIM.
* Digest Value, Event Type, index, RIM Tagid
*/
@ToString @EqualsAndHashCode(callSuper = false)
@Setter @Getter
@Entity
@Table(name = "ReferenceDigestValue")
@Access(AccessType.FIELD)
public class ReferenceDigestValue extends ArchivableEntity {
// @Type(type = "uuid-char")
@JdbcTypeCode(java.sql.Types.VARCHAR)
@Column
private UUID baseRimId;
// @Type(type = "uuid-char")
@JdbcTypeCode(java.sql.Types.VARCHAR)
@Column
private UUID supportRimId;
@Column(nullable = false)
private String manufacturer;
@Column(nullable = false)
private String model;
@Column(nullable = false)
private int pcrIndex;
@Column(nullable = false)
private String digestValue;
@Column(nullable = false)
private String eventType;
@Column(columnDefinition = "blob", nullable = true)
private byte[] contentBlob;
@Column(nullable = false)
private boolean matchFail;
@Column(nullable = false)
private boolean patched = false;
@Column(nullable = false)
private boolean updated = false;
/**
* Default constructor necessary for Hibernate.
*/
protected ReferenceDigestValue() {
super();
this.baseRimId = null;
this.supportRimId = null;
this.manufacturer = "";
this.model = "";
this.pcrIndex = -1;
this.digestValue = "";
this.eventType = "";
this.matchFail = false;
this.patched = false;
this.updated = false;
this.contentBlob = null;
}
}

View File

@ -92,7 +92,6 @@ public class ReferenceManifest extends ArchivableEntity {
private String platformModel = null; private String platformModel = null;
@Column(nullable = false) @Column(nullable = false)
private String fileName = null; private String fileName = null;
// @Type(type="uuid-char")
@JdbcTypeCode(java.sql.Types.VARCHAR) @JdbcTypeCode(java.sql.Types.VARCHAR)
@Column @Column
private UUID associatedRim; private UUID associatedRim;

View File

@ -0,0 +1,124 @@
package hirs.attestationca.persist.entity.userdefined;
import com.google.common.base.Preconditions;
import hirs.attestationca.persist.entity.ArchivableEntity;
import hirs.attestationca.persist.enums.AppraisalStatus;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.JoinTable;
import jakarta.persistence.ManyToMany;
import lombok.Getter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* Stores results of a single element of the supply chain validation process.
*/
@Entity
public class SupplyChainValidation extends ArchivableEntity {
/**
* Used to indicate which type of validation a result is related to.
*/
public enum ValidationType {
/**
* Validation of an endorsement credential.
*/
ENDORSEMENT_CREDENTIAL,
/**
* Validation of a platform credential and also delta platform credentials from spec 1.1.
*/
PLATFORM_CREDENTIAL,
/**
* Validation of a platform credential's attributes.
*/
PLATFORM_CREDENTIAL_ATTRIBUTES,
/**
* Validation of the device firmware.
*/
FIRMWARE
}
@Getter
@Column
private final ValidationType validationType;
@Getter
@Column
private final AppraisalStatus.Status validationResult;
@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(name = "CertificatesUsedToValidate",
joinColumns = { @JoinColumn(name = "validation_id", nullable = false) })
private final List<Certificate> certificatesUsed;
@Getter
@Column(length = MAX_MESSAGE_LENGTH)
private final String message;
@Getter
@Column
private String rimId;
/**
* Default constructor necessary for Hibernate.
*/
public SupplyChainValidation() {
this.validationType = null;
this.validationResult = AppraisalStatus.Status.ERROR;
this.certificatesUsed = Collections.emptyList();
this.message = null;
this.rimId = "";
}
/**
* Construct a new SupplyChainValidation instance.
*
* @param validationType the type of validation this instance will represent; not null
* @param validationResult whether the validation was successful or not
* @param certificatesUsed certificates used, if any, in the validation process; not null
* @param message a related information or error message; may be null
*/
public SupplyChainValidation(final ValidationType validationType,
final AppraisalStatus.Status validationResult,
final List<ArchivableEntity> certificatesUsed,
final String message) {
Preconditions.checkArgument(
validationType != null,
"Cannot construct a SupplyChainValidation with a null ValidationType"
);
Preconditions.checkArgument(
certificatesUsed != null,
"Cannot construct a SupplyChainValidation with a null certificatesUsed"
);
this.validationType = validationType;
this.validationResult = validationResult;
this.certificatesUsed = new ArrayList<>();
this.rimId = "";
for (ArchivableEntity ae : certificatesUsed) {
if (ae instanceof ReferenceManifest) {
this.rimId = ae.getId().toString();
break;
} else {
this.certificatesUsed.add((Certificate) ae);
}
}
this.message = message;
}
/**
* @return certificates used in the process of performing the validation; may be empty
*/
public List<Certificate> getCertificatesUsed() {
return Collections.unmodifiableList(certificatesUsed);
}
}

View File

@ -0,0 +1,267 @@
package hirs.attestationca.persist.entity.userdefined;
import com.google.common.base.Preconditions;
import hirs.attestationca.persist.entity.ArchivableEntity;
import hirs.attestationca.persist.enums.AppraisalStatus;
import jakarta.persistence.CascadeType;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.FetchType;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.OneToMany;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.CriteriaQuery;
import jakarta.persistence.criteria.Predicate;
import jakarta.persistence.criteria.Root;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.util.Strings;
import org.springframework.data.repository.CrudRepository;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
/**
* A container class to group multiple related {@link SupplyChainValidation} instances
* together.
*/
@Entity
public class SupplyChainValidationSummary extends ArchivableEntity {
@ManyToOne
@JoinColumn(name = "device_id")
private final Device device;
private static final String DEVICE_ID_FIELD = "device.id";
@Column
@Enumerated(EnumType.STRING)
private final AppraisalStatus.Status overallValidationResult;
@Column(length = RESULT_MESSAGE_LENGTH)
private final String message;
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER,
targetEntity = SupplyChainValidation.class, orphanRemoval = true)
private final Set<SupplyChainValidation> validations;
/**
* Default constructor necessary for Hibernate.
*/
protected SupplyChainValidationSummary() {
this.device = null;
overallValidationResult = AppraisalStatus.Status.FAIL;
validations = Collections.emptySet();
this.message = Strings.EMPTY;
}
/**
* This class enables the retrieval of SupplyChainValidationSummaries by their attributes.
*/
public static class Selector {
private final CrudRepository<SupplyChainValidationSummary, UUID>
supplyChainValidationSummaryCrudManager;
private final Map<String, Object> fieldValueSelections;
/**
* Construct a new Selector that will use the given {@link CrudRepository} to
* retrieve SupplyChainValidationSummaries.
*
* @param supplyChainValidationSummaryCrudManager the summary manager to be used to retrieve
* supply chain validation summaries
*/
public Selector(
final CrudRepository<SupplyChainValidationSummary, UUID>
supplyChainValidationSummaryCrudManager) {
Preconditions.checkArgument(
supplyChainValidationSummaryCrudManager != null,
"supply chain validation summary manager cannot be null"
);
this.supplyChainValidationSummaryCrudManager = supplyChainValidationSummaryCrudManager;
this.fieldValueSelections = new HashMap<>();
}
/**
* Construct the criterion that can be used to query for supply chain validation summaries
* matching the configuration of this Selector.
*
* @return a Criterion that can be used to query for supply chain validation summaries
* matching the configuration of this instance
*/
public Predicate[] getCriterion(final CriteriaBuilder criteriaBuilder) {
Predicate[] predicates = new Predicate[fieldValueSelections.size()];
CriteriaQuery<SupplyChainValidationSummary> query = criteriaBuilder.createQuery(SupplyChainValidationSummary.class);
Root<SupplyChainValidationSummary> root = query.from(SupplyChainValidationSummary.class);
int i = 0;
for (Map.Entry<String, Object> fieldValueEntry : fieldValueSelections.entrySet()) {
predicates[i++] = criteriaBuilder.equal(root.get(fieldValueEntry.getKey()), fieldValueEntry.getValue());
}
return predicates;
}
/**
* Set a field name and value to match.
*
* @param name the field name to query
* @param value the value to query
*/
protected void setFieldValue(final String name, final Object value) {
Object valueToAssign = value;
Preconditions.checkArgument(
value != null,
"field value cannot be null."
);
if (value instanceof String) {
Preconditions.checkArgument(
StringUtils.isNotEmpty((String) value),
"field value cannot be empty."
);
}
if (value instanceof byte[]) {
byte[] valueBytes = (byte[]) value;
Preconditions.checkArgument(
ArrayUtils.isNotEmpty(valueBytes),
"field value cannot be empty."
);
valueToAssign = Arrays.copyOf(valueBytes, valueBytes.length);
}
fieldValueSelections.put(name, valueToAssign);
}
/**
* Specify a device id that supply chain validation summaries must have to be considered
* as matching.
*
* @param device the device id to query
* @return this instance (for chaining further calls)
*/
public Selector byDeviceId(final UUID device) {
setFieldValue(DEVICE_ID_FIELD, device);
return this;
}
}
/**
* Get a Selector for use in retrieving SupplyChainValidationSummary.
*
* @param certMan the CrudManager to be used to retrieve persisted supply chain validation
* summaries
* @return a SupplyChainValidationSummary.Selector instance to use for retrieving certificates
*/
public static SupplyChainValidationSummary.Selector select(
final CrudRepository<SupplyChainValidationSummary, UUID> certMan) {
return new SupplyChainValidationSummary.Selector(certMan);
}
/**
* Construct a new SupplyChainValidationSummary.
*
* @param device device that underwent supply chain validation
* @param validations a Collection of Validations that should comprise this summary; not null
*/
public SupplyChainValidationSummary(final Device device,
final Collection<SupplyChainValidation> validations) {
Preconditions.checkArgument(
device != null,
"Cannot construct a SupplyChainValidationSummary with a null device"
);
Preconditions.checkArgument(
validations != null,
"Cannot construct a SupplyChainValidationSummary with a null validations list"
);
this.device = device;
AppraisalStatus status = calculateValidationResult(validations);
this.overallValidationResult = status.getAppStatus();
this.validations = new HashSet<>(validations);
this.message = status.getMessage();
}
/**
* This retrieves the device associated with the supply chain validation summaries.
*
* @return the validated device
*/
public Device getDevice() {
return device;
}
/**
* @return the overall appraisal result
*/
public AppraisalStatus.Status getOverallValidationResult() {
return overallValidationResult;
}
/**
* @return the fail message if there is a failure.
*/
public String getMessage() {
return message;
}
/**
* @return the validations that this summary contains
*/
public Set<SupplyChainValidation> getValidations() {
return Collections.unmodifiableSet(validations);
}
/**
* A utility method that helps determine the overall appraisal result.
*
* @param validations the validations to evaluate
* @return the overall appraisal result
*/
private AppraisalStatus calculateValidationResult(
final Collection<SupplyChainValidation> validations) {
boolean hasAnyFailures = false;
StringBuilder failureMsg = new StringBuilder();
for (SupplyChainValidation validation : validations) {
switch (validation.getValidationResult()) {
// if any error, then process overall as error immediately.
case ERROR:
return new AppraisalStatus(AppraisalStatus.Status.ERROR,
validation.getMessage());
case FAIL:
hasAnyFailures = true;
failureMsg.append(String.format("%s%n", validation.getValidationType()));
break;
default:
break;
}
}
// if failures, but no error, indicate failure result.
if (hasAnyFailures) {
return new AppraisalStatus(AppraisalStatus.Status.FAIL,
failureMsg.toString());
}
return new AppraisalStatus(AppraisalStatus.Status.PASS,
Strings.EMPTY);
}
}

View File

@ -1,6 +1,8 @@
package hirs.attestationca.persist.entity.userdefined.certificate; package hirs.attestationca.persist.entity.userdefined.certificate;
import hirs.attestationca.persist.entity.userdefined.Certificate; import hirs.attestationca.persist.entity.userdefined.Certificate;
import hirs.attestationca.persist.service.CertificateService;
import hirs.attestationca.persist.service.selector.CertificateSelector;
import jakarta.persistence.Column; import jakarta.persistence.Column;
import jakarta.persistence.Entity; import jakarta.persistence.Entity;
import lombok.Getter; import lombok.Getter;
@ -44,6 +46,43 @@ public class CertificateAuthorityCredential extends Certificate {
@Column @Column
private final String credentialType = "TCPA Trusted Platform Module Endorsement"; private final String credentialType = "TCPA Trusted Platform Module Endorsement";
/**
* This class enables the retrieval of CertificateAuthorityCredentials by their attributes.
*/
public static class Selector extends CertificateSelector<CertificateAuthorityCredential> {
/**
* Construct a new CertificateSelector that will use the given {@link CertificateService} to
* retrieve one or many CertificateAuthorityCredentials.
*
* @param certificateManager the certificate manager to be used to retrieve certificates
*/
public Selector(final CertificateService certificateManager) {
super(certificateManager, CertificateAuthorityCredential.class);
}
/**
* Specify a subject key identifier that certificates must have to be considered
* as matching.
*
* @param subjectKeyIdentifier a subject key identifier buffer to query, not empty or null
* @return this instance (for chaining further calls)
*/
public Selector bySubjectKeyIdentifier(final byte[] subjectKeyIdentifier) {
setFieldValue(SUBJECT_KEY_IDENTIFIER_FIELD, subjectKeyIdentifier);
return this;
}
}
/**
* Get a Selector for use in retrieving CertificateAuthorityCredentials.
*
* @param certMan the CertificateService to be used to retrieve persisted certificates
* @return a CertificateAuthorityCredential.Selector instance to use for retrieving certificates
*/
public static Selector select(final CertificateService certMan) {
return new Selector(certMan);
}
/** /**
* Construct a new CertificateAuthorityCredential given its binary contents. The given * Construct a new CertificateAuthorityCredential given its binary contents. The given
* certificate should represent either an X509 certificate or X509 attribute certificate. * certificate should represent either an X509 certificate or X509 attribute certificate.

View File

@ -0,0 +1,41 @@
package hirs.attestationca.persist.entity.userdefined.certificate;
import hirs.attestationca.persist.entity.AbstractEntity;
import jakarta.persistence.Entity;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import java.util.Objects;
import java.util.UUID;
@EqualsAndHashCode(callSuper=false)
@Getter
@Entity
public class ComponentResult extends AbstractEntity {
private UUID certificateId;
private int componentHash;
private String expected;
private String actual;
private boolean mismatched;
/**
* Hibernate default constructor
*/
protected ComponentResult() {
}
public ComponentResult(final UUID certificateId, final int componentHash,
final String expected, final String actual) {
this.certificateId = certificateId;
this.componentHash = componentHash;
this.expected = expected;
this.actual = actual;
this.mismatched = Objects.equals(expected, actual);
}
public String toString() {
return String.format("ComponentResult[%d]: expected=[%s] actual=[%s]",
componentHash, expected, actual);
}
}

View File

@ -7,6 +7,8 @@ import hirs.attestationca.persist.entity.userdefined.certificate.attributes.Plat
import hirs.attestationca.persist.entity.userdefined.certificate.attributes.TBBSecurityAssertion; import hirs.attestationca.persist.entity.userdefined.certificate.attributes.TBBSecurityAssertion;
import hirs.attestationca.persist.entity.userdefined.certificate.attributes.URIReference; import hirs.attestationca.persist.entity.userdefined.certificate.attributes.URIReference;
import hirs.attestationca.persist.entity.userdefined.certificate.attributes.V2.PlatformConfigurationV2; import hirs.attestationca.persist.entity.userdefined.certificate.attributes.V2.PlatformConfigurationV2;
import hirs.attestationca.persist.service.CertificateService;
import hirs.attestationca.persist.service.selector.CertificateSelector;
import jakarta.persistence.Column; import jakarta.persistence.Column;
import jakarta.persistence.Entity; import jakarta.persistence.Entity;
import jakarta.persistence.Transient; import jakarta.persistence.Transient;
@ -46,6 +48,7 @@ import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.UUID;
/** /**
* This class persists Platform credentials by extending the base Certificate * This class persists Platform credentials by extending the base Certificate
@ -94,7 +97,7 @@ public class PlatformCredential extends DeviceAssociatedCertificate {
* TCG Platform Specification values * TCG Platform Specification values
* At this time these are placeholder values. * At this time these are placeholder values.
*/ */
private static final Map<String, String> TCG_PLATFORM_MAP = new HashMap<String, String>() {{ private static final Map<String, String> TCG_PLATFORM_MAP = new HashMap<>() {{
put("#00000000", "Unclassified"); put("#00000000", "Unclassified");
put("#00000001", "PC Client"); put("#00000001", "PC Client");
put("#00000002", "PDA"); put("#00000002", "PDA");
@ -128,89 +131,89 @@ public class PlatformCredential extends DeviceAssociatedCertificate {
/** /**
* This class enables the retrieval of PlatformCredentials by their attributes. * This class enables the retrieval of PlatformCredentials by their attributes.
*/ */
// public static class Selector extends CertificateSelector<PlatformCredential> { public static class Selector extends CertificateSelector<PlatformCredential> {
// /** /**
// * Construct a new CertificateSelector that will use the given {@link CertificateManager} to * Construct a new CertificateSelector that will use the given {@link CertificateService} to
// * retrieve one or many PlatformCredentials. * retrieve one or many PlatformCredentials.
// * *
// * @param certificateManager the certificate manager to be used to retrieve certificates * @param certificateManager the certificate manager to be used to retrieve certificates
// */ */
// public Selector(final CertificateManager certificateManager) { public Selector(final CertificateService certificateManager) {
// super(certificateManager, PlatformCredential.class); super(certificateManager, PlatformCredential.class);
// } }
//
// /** /**
// * Specify a manufacturer that certificates must have to be considered as matching. * Specify a manufacturer that certificates must have to be considered as matching.
// * @param manufacturer the manufacturer to query, not empty or null * @param manufacturer the manufacturer to query, not empty or null
// * @return this instance (for chaining further calls) * @return this instance (for chaining further calls)
// */ */
// public Selector byManufacturer(final String manufacturer) { public Selector byManufacturer(final String manufacturer) {
// setFieldValue(MANUFACTURER_FIELD, manufacturer); setFieldValue(MANUFACTURER_FIELD, manufacturer);
// return this; return this;
// } }
//
// /** /**
// * Specify a model that certificates must have to be considered as matching. * Specify a model that certificates must have to be considered as matching.
// * @param model the model to query, not empty or null * @param model the model to query, not empty or null
// * @return this instance (for chaining further calls) * @return this instance (for chaining further calls)
// */ */
// public Selector byModel(final String model) { public Selector byModel(final String model) {
// setFieldValue(MODEL_FIELD, model); setFieldValue(MODEL_FIELD, model);
// return this; return this;
// } }
//
// /** /**
// * Specify a version that certificates must have to be considered as matching. * Specify a version that certificates must have to be considered as matching.
// * @param version the version to query, not empty or null * @param version the version to query, not empty or null
// * @return this instance (for chaining further calls) * @return this instance (for chaining further calls)
// */ */
// public Selector byVersion(final String version) { public Selector byVersion(final String version) {
// setFieldValue(VERSION_FIELD, version); setFieldValue(VERSION_FIELD, version);
// return this; return this;
// } }
//
// /** /**
// * Specify a serial number that certificates must have to be considered as matching. * Specify a serial number that certificates must have to be considered as matching.
// * @param serialNumber the serial number to query, not empty or null * @param serialNumber the serial number to query, not empty or null
// * @return this instance (for chaining further calls) * @return this instance (for chaining further calls)
// */ */
// public Selector bySerialNumber(final String serialNumber) { public Selector bySerialNumber(final String serialNumber) {
// setFieldValue(SERIAL_NUMBER_FIELD, serialNumber); setFieldValue(SERIAL_NUMBER_FIELD, serialNumber);
// return this; return this;
// } }
//
// /** /**
// * Specify a board serial number that certificates must have to be considered as matching. * Specify a board serial number that certificates must have to be considered as matching.
// * @param boardSerialNumber the board serial number to query, not empty or null * @param boardSerialNumber the board serial number to query, not empty or null
// * @return this instance (for chaining further calls) * @return this instance (for chaining further calls)
// */ */
// public Selector byBoardSerialNumber(final String boardSerialNumber) { public Selector byBoardSerialNumber(final String boardSerialNumber) {
// setFieldValue(PLATFORM_SERIAL_FIELD, boardSerialNumber); setFieldValue(PLATFORM_SERIAL_FIELD, boardSerialNumber);
// return this; return this;
// } }
//
// /** /**
// * Specify a chassis serial number that certificates must have to be considered as matching. * Specify a chassis serial number that certificates must have to be considered as matching.
// * @param chassisSerialNumber the board serial number to query, not empty or null * @param chassisSerialNumber the board serial number to query, not empty or null
// * @return this instance (for chaining further calls) * @return this instance (for chaining further calls)
// */ */
// public Selector byChassisSerialNumber(final String chassisSerialNumber) { public Selector byChassisSerialNumber(final String chassisSerialNumber) {
// setFieldValue(CHASSIS_SERIAL_NUMBER_FIELD, chassisSerialNumber); setFieldValue(CHASSIS_SERIAL_NUMBER_FIELD, chassisSerialNumber);
// return this; return this;
// } }
//
// /** /**
// * Specify a device id that certificates must have to be considered * Specify a device id that certificates must have to be considered
// * as matching. * as matching.
// * *
// * @param device the device id to query * @param device the device id to query
// * @return this instance (for chaining further calls) * @return this instance (for chaining further calls)
// */ */
// public Selector byDeviceId(final UUID device) { public Selector byDeviceId(final UUID device) {
// setFieldValue(DEVICE_ID_FIELD, device); setFieldValue(DEVICE_ID_FIELD, device);
// return this; return this;
// } }
// } }
@Column @Column
private String credentialType = null; private String credentialType = null;
@ -275,9 +278,9 @@ public class PlatformCredential extends DeviceAssociatedCertificate {
* @param certMan the CertificateManager to be used to retrieve persisted certificates * @param certMan the CertificateManager to be used to retrieve persisted certificates
* @return a PlatformCredential.Selector instance to use for retrieving certificates * @return a PlatformCredential.Selector instance to use for retrieving certificates
*/ */
// public static Selector select(final CertificateManager certMan) { public static Selector select(final CertificateService certMan) {
// return new Selector(certMan); return new Selector(certMan);
// } }
/** /**
* Construct a new PlatformCredential given its binary contents. ParseFields is * Construct a new PlatformCredential given its binary contents. ParseFields is

View File

@ -52,6 +52,7 @@ public class ComponentClass {
@Getter @Getter
private String component, componentStr; private String component, componentStr;
private String registryType; private String registryType;
@Getter
private String componentIdentifier; private String componentIdentifier;
/** /**

View File

@ -0,0 +1,221 @@
package hirs.attestationca.persist.entity.userdefined.info;
import jakarta.persistence.Column;
import jakarta.persistence.DiscriminatorColumn;
import jakarta.persistence.DiscriminatorType;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.xml.bind.annotation.XmlElement;
import lombok.NoArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.hibernate.annotations.DiscriminatorOptions;
import org.springframework.util.Assert;
import java.io.Serializable;
import java.util.Objects;
/**
* ComponentInfo is a class to hold Hardware component information
* such as manufacturer, model, serial number and version.
*/
@NoArgsConstructor
@Entity
@DiscriminatorColumn(name = "componentTypeEnum", discriminatorType = DiscriminatorType.STRING)
@DiscriminatorOptions(force = true)
public class ComponentInfo implements Serializable {
@Id
@Column(name = "componentInfo_id")
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@XmlElement
@Column(nullable = false)
private String componentManufacturer;
@XmlElement
@Column(nullable = false)
private String componentModel;
@XmlElement
@Column
private String componentSerial;
@XmlElement
@Column
private String componentRevision;
@XmlElement
@Column
private String componentClass;
/**
* Get the Component's Manufacturer.
* @return the Component's Manufacturer
*/
public String getComponentManufacturer() {
return componentManufacturer;
}
/**
* Get the Component's Model.
* @return the Component's Model
*/
public String getComponentModel() {
return componentModel;
}
/**
* Get the Component's Serial Number.
* @return the Component's Serial Number
*/
public String getComponentSerial() {
return componentSerial;
}
/**
* Get the Component's Revision.
* @return the Component's Revision
*/
public String getComponentRevision() {
return componentRevision;
}
/**
* Get the Component's Class Registry.
* @return the Component's Class
*/
public String getComponentClass() {
return componentClass;
}
/**
* Constructor.
* @param componentManufacturer Component Manufacturer (must not be null)
* @param componentModel Component Model (must not be null)
* @param componentSerial Component Serial Number (can be null)
* @param componentRevision Component Revision or Version (can be null)
*/
public ComponentInfo(final String componentManufacturer,
final String componentModel,
final String componentSerial,
final String componentRevision) {
Assert.state(isComplete(
componentManufacturer,
componentModel,
componentSerial,
componentRevision),
"ComponentInfo: manufacturer and/or "
+ "model can not be null");
this.componentManufacturer = componentManufacturer.trim();
this.componentModel = componentModel.trim();
if (componentSerial != null) {
this.componentSerial = componentSerial.trim();
} else {
this.componentSerial = StringUtils.EMPTY;
}
if (componentRevision != null) {
this.componentRevision = componentRevision.trim();
} else {
this.componentRevision = StringUtils.EMPTY;
}
}
/**
* Constructor.
* @param componentManufacturer Component Manufacturer (must not be null)
* @param componentModel Component Model (must not be null)
* @param componentSerial Component Serial Number (can be null)
* @param componentRevision Component Revision or Version (can be null)
* @param componentClass Component Class (can be null)
*/
public ComponentInfo(final String componentManufacturer,
final String componentModel,
final String componentSerial,
final String componentRevision,
final String componentClass) {
Assert.state(isComplete(
componentManufacturer,
componentModel,
componentSerial,
componentRevision),
"ComponentInfo: manufacturer and/or "
+ "model can not be null");
this.componentManufacturer = componentManufacturer.trim();
this.componentModel = componentModel.trim();
if (componentSerial != null) {
this.componentSerial = componentSerial.trim();
} else {
this.componentSerial = StringUtils.EMPTY;
}
if (componentRevision != null) {
this.componentRevision = componentRevision.trim();
} else {
this.componentRevision = StringUtils.EMPTY;
}
if (componentClass != null) {
this.componentClass = componentClass;
} else {
this.componentClass = StringUtils.EMPTY;
}
}
/**
* Determines whether the given properties represent a
* ComponentInfo that will be useful in validation.
* Currently, only components which have a non-null
* manufacturer and model are considered valid.
*
* @param componentManufacturer a String containing a component's manufacturer
* @param componentModel a String representing a component's model
* @param componentSerial a String representing a component's serial number
* @param componentRevision a String representing a component's revision
* @return true if the component is valid, false if not
*/
public static boolean isComplete(final String componentManufacturer,
final String componentModel,
final String componentSerial,
final String componentRevision) {
return !(StringUtils.isEmpty(componentManufacturer)
|| StringUtils.isEmpty(componentModel));
}
@Override
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
ComponentInfo that = (ComponentInfo) o;
return Objects.equals(id, that.id)
&& Objects.equals(componentManufacturer, that.componentManufacturer)
&& Objects.equals(componentModel, that.componentModel)
&& Objects.equals(componentSerial, that.componentSerial)
&& Objects.equals(componentRevision, that.componentRevision)
&& Objects.equals(componentClass, that.componentClass);
}
@Override
public int hashCode() {
return Objects.hash(id, componentManufacturer, componentModel,
componentSerial, componentRevision, componentClass);
}
@Override
public String toString() {
return String.format("ComponentInfo{"
+ "componentManufacturer='%s'"
+ ", componentModel='%s'"
+ ", componentSerial='%s'"
+ ", componentRevision='%s'"
+ ", componentClass='%s'}",
componentManufacturer,
componentModel, componentSerial,
componentRevision, componentClass);
}
}

View File

@ -2,7 +2,9 @@ package hirs.attestationca.persist.entity.userdefined.rim;
import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnore;
import hirs.attestationca.persist.entity.userdefined.ReferenceManifest; import hirs.attestationca.persist.entity.userdefined.ReferenceManifest;
import hirs.attestationca.persist.service.ReferenceManifestService;
import hirs.attestationca.persist.service.ReferenceManifestServiceImpl; import hirs.attestationca.persist.service.ReferenceManifestServiceImpl;
import hirs.attestationca.persist.service.selector.ReferenceManifestSelector;
import hirs.utils.SwidResource; import hirs.utils.SwidResource;
import hirs.utils.xjc.BaseElement; import hirs.utils.xjc.BaseElement;
import hirs.utils.xjc.Directory; import hirs.utils.xjc.Directory;
@ -93,6 +95,105 @@ public class BaseReferenceManifest extends ReferenceManifest {
private String linkHref = null; private String linkHref = null;
private String linkRel = null; private String linkRel = null;
/**
* This class enables the retrieval of BaseReferenceManifest by their attributes.
*/
public static class Selector extends ReferenceManifestSelector<BaseReferenceManifest> {
/**
* Construct a new ReferenceManifestSelector that will use
* the given (@link ReferenceManifestService}
* to retrieve one or may BaseReferenceManifest.
*
* @param referenceManifestManager the reference manifest manager to be used to retrieve
* reference manifests.
*/
public Selector(final ReferenceManifestService referenceManifestManager) {
super(referenceManifestManager, BaseReferenceManifest.class);
}
/**
* Specify the platform manufacturer that rims must have to be considered
* as matching.
* @param manufacturer string for the manufacturer
* @return this instance
*/
public Selector byManufacturer(final String manufacturer) {
setFieldValue(PLATFORM_MANUFACTURER, manufacturer);
return this;
}
/**
* Specify the platform model that rims must have to be considered
* as matching.
* @param model string for the model
* @return this instance
*/
public Selector byModel(final String model) {
setFieldValue(PLATFORM_MODEL, model);
return this;
}
/**
* Specify the platform manufacturer/model that rims must have to be considered
* as matching.
* @param manufacturer string for the manufacturer
* @param model string for the model
* @return this instance
*/
public Selector byManufacturerModel(final String manufacturer, final String model) {
setFieldValue(PLATFORM_MANUFACTURER, manufacturer);
setFieldValue(PLATFORM_MODEL, model);
return this;
}
/**
* Specify the platform manufacturer/model/base flag that rims must have to be considered
* as matching.
* @param manufacturer string for the manufacturer
* @param model string for the model
* @return this instance
*/
public Selector byManufacturerModelBase(final String manufacturer, final String model) {
setFieldValue(PLATFORM_MANUFACTURER, manufacturer);
setFieldValue(PLATFORM_MODEL, model);
setFieldValue("swidPatch", false);
setFieldValue("swidSupplemental", false);
//setFieldValue("", false); //corpus?
return this;
}
/**
* Specify the device name that rims must have to be considered
* as matching.
* @param deviceName string for the deviceName
* @return this instance
*/
public Selector byDeviceName(final String deviceName) {
setFieldValue("deviceName", deviceName);
return this;
}
/**
* Specify the RIM hash associated with the base RIM.
* @param base64Hash the hash of the file associated with the rim
* @return this instance
*/
public Selector byBase64Hash(final String base64Hash) {
setFieldValue(BASE_64_HASH_FIELD, base64Hash);
return this;
}
/**
* Specify the RIM hash associated with the base RIM.
* @param hexDecHash the hash of the file associated with the rim
* @return this instance
*/
public Selector byHexDecHash(final String hexDecHash) {
setFieldValue(HEX_DEC_HASH_FIELD, hexDecHash);
return this;
}
}
/** /**
* Support constructor for the RIM object. * Support constructor for the RIM object.
* *
@ -242,6 +343,17 @@ public class BaseReferenceManifest extends ReferenceManifest {
} }
} }
/**
* Get a Selector for use in retrieving ReferenceManifest.
*
* @param rimMan the ReferenceManifestService to be used to retrieve
* persisted RIMs
* @return a Selector instance to use for retrieving RIMs
*/
public static Selector select(final ReferenceManifestService rimMan) {
return new Selector(rimMan);
}
/** /**
* This method and code is pulled and adopted from the TCG Tool. Since this * This method and code is pulled and adopted from the TCG Tool. Since this
* is taking in an file stored in memory through http, this was changed from * is taking in an file stored in memory through http, this was changed from

View File

@ -3,20 +3,33 @@ package hirs.attestationca.persist.entity.userdefined.rim;
import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnore;
import hirs.attestationca.persist.entity.userdefined.ReferenceManifest; import hirs.attestationca.persist.entity.userdefined.ReferenceManifest;
import hirs.attestationca.persist.enums.AppraisalStatus; import hirs.attestationca.persist.enums.AppraisalStatus;
import hirs.attestationca.persist.service.ReferenceManifestService;
import hirs.attestationca.persist.service.selector.ReferenceManifestSelector;
import hirs.utils.tpm.eventlog.TCGEventLog;
import hirs.utils.tpm.eventlog.TpmPcrEvent;
import jakarta.persistence.Column; import jakarta.persistence.Column;
import jakarta.persistence.Entity; import jakarta.persistence.Entity;
import jakarta.persistence.EnumType; import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated; import jakarta.persistence.Enumerated;
import lombok.EqualsAndHashCode;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import lombok.extern.log4j.Log4j2;
import java.io.IOException; import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
/** /**
* Sub class that will just focus on PCR Values and Events. * Sub class that will just focus on PCR Values and Events.
* Similar to {@link SupportReferenceManifest} * Similar to {@link SupportReferenceManifest}
* however this is the live log from the client. * however this is the live log from the client.
*/ */
@Log4j2
@EqualsAndHashCode(callSuper=false)
@Entity @Entity
public class EventLogMeasurements extends ReferenceManifest { public class EventLogMeasurements extends ReferenceManifest {
@ -28,6 +41,66 @@ public class EventLogMeasurements extends ReferenceManifest {
@Getter @Setter @Getter @Setter
private AppraisalStatus.Status overallValidationResult = AppraisalStatus.Status.FAIL; private AppraisalStatus.Status overallValidationResult = AppraisalStatus.Status.FAIL;
/**
* This class enables the retrieval of SupportReferenceManifest by their attributes.
*/
public static class Selector extends ReferenceManifestSelector<EventLogMeasurements> {
/**
* Construct a new ReferenceManifestSelector that
* will use the given (@link ReferenceManifestService}
* to retrieve one or may SupportReferenceManifest.
*
* @param referenceManifestManager the reference manifest manager to be used to retrieve
* reference manifests.
*/
public Selector(final ReferenceManifestService referenceManifestManager) {
super(referenceManifestManager, EventLogMeasurements.class, false);
}
/**
* Specify the platform manufacturer that rims must have to be considered
* as matching.
* @param manufacturer string for the manufacturer
* @return this instance
*/
public Selector byManufacturer(final String manufacturer) {
setFieldValue(PLATFORM_MANUFACTURER, manufacturer);
return this;
}
/**
* Specify the platform model that rims must have to be considered
* as matching.
* @param model string for the model
* @return this instance
*/
public Selector byModel(final String model) {
setFieldValue(PLATFORM_MODEL, model);
return this;
}
/**
* Specify the device name that rims must have to be considered
* as matching.
* @param deviceName string for the deviceName
* @return this instance
*/
public Selector byDeviceName(final String deviceName) {
setFieldValue("deviceName", deviceName);
return this;
}
/**
* Specify the RIM hash associated with the Event Log.
* @param hexDecHash the hash of the file associated with the rim
* @return this instance
*/
public Selector byHexDecHash(final String hexDecHash) {
setFieldValue(HEX_DEC_HASH_FIELD, hexDecHash);
return this;
}
}
/** /**
* Support constructor for the RIM object. * Support constructor for the RIM object.
* *
@ -61,4 +134,57 @@ public class EventLogMeasurements extends ReferenceManifest {
super(); super();
this.pcrHash = 0; this.pcrHash = 0;
} }
/**
* Get a Selector for use in retrieving ReferenceManifest.
*
* @param rimMan the ReferenceManifestService to be used to retrieve
* persisted RIMs
* @return a Selector instance to use for retrieving RIMs
*/
public static Selector select(final ReferenceManifestService rimMan) {
return new Selector(rimMan);
}
/**
* Getter method for the expected PCR values contained within the support
* RIM.
* @return a string array of the pcr values.
*/
public String[] getExpectedPCRList() {
try {
TCGEventLog logProcessor = new TCGEventLog(this.getRimBytes());
this.pcrHash = Arrays.hashCode(logProcessor.getExpectedPCRValues());
return logProcessor.getExpectedPCRValues();
} catch (CertificateException cEx) {
log.error(cEx);
} catch (NoSuchAlgorithmException noSaEx) {
log.error(noSaEx);
} catch (IOException ioEx) {
log.error(ioEx);
}
return new String[0];
}
/**
* Getter method for the event log that should be present in the support RIM.
*
* @return list of TPM PCR Events for display
*/
public Collection<TpmPcrEvent> getEventLog() {
TCGEventLog logProcessor = null;
try {
logProcessor = new TCGEventLog(this.getRimBytes());
return logProcessor.getEventList();
} catch (CertificateException cEx) {
log.error(cEx);
} catch (NoSuchAlgorithmException noSaEx) {
log.error(noSaEx);
} catch (IOException ioEx) {
log.error(ioEx);
}
return new ArrayList<>();
}
} }

View File

@ -0,0 +1,135 @@
package hirs.attestationca.persist.entity.userdefined.rim;
import hirs.attestationca.persist.entity.ArchivableEntity;
import jakarta.persistence.Access;
import jakarta.persistence.AccessType;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.bouncycastle.util.Arrays;
import org.hibernate.annotations.JdbcTypeCode;
import java.util.UUID;
/**
* This class represents that actual entry in the Support RIM.
* Digest Value, Event Type, index, RIM Tagid
*/
@Data
@Builder
@AllArgsConstructor
@Entity
@EqualsAndHashCode(callSuper=false)
@Table(name = "ReferenceDigestValue")
@Access(AccessType.FIELD)
public class ReferenceDigestValue extends ArchivableEntity {
@JdbcTypeCode(java.sql.Types.VARCHAR)
@Column
private UUID baseRimId;
@JdbcTypeCode(java.sql.Types.VARCHAR)
@Column
private UUID supportRimId;
@Column(nullable = false)
private String manufacturer;
@Column(nullable = false)
private String model;
@Column(nullable = false)
private int pcrIndex;
@Column(nullable = false)
private String digestValue;
@Column(nullable = false)
private String eventType;
@Column(columnDefinition = "blob", nullable = true)
private byte[] contentBlob;
@Column(nullable = false)
private boolean matchFail;
@Column(nullable = false)
private boolean patched;
@Column(nullable = false)
private boolean updated;
/**
* Default constructor necessary for Hibernate.
*/
protected ReferenceDigestValue() {
super();
this.baseRimId = null;
this.supportRimId = null;
this.manufacturer = "";
this.model = "";
this.pcrIndex = -1;
this.digestValue = "";
this.eventType = "";
this.matchFail = false;
this.patched = false;
this.updated = false;
this.contentBlob = null;
}
/**
* Default Constructor with parameters for all associated data.
* @param baseRimId the UUID of the associated record
* @param supportRimId the UUID of the associated record
* @param manufacturer associated creator for this information
* @param model the specific device type
* @param pcrIndex the event number
* @param digestValue the key digest value
* @param eventType the event type to store
* @param matchFail the status of the baseline check
* @param patched the status of the value being updated to patch
* @param updated the status of the value being updated with info
* @param contentBlob the data value of the content
*/
public ReferenceDigestValue(final UUID baseRimId, final UUID supportRimId,
final String manufacturer, final String model,
final int pcrIndex, final String digestValue,
final String eventType, final boolean matchFail,
final boolean patched, final boolean updated,
final byte[] contentBlob) {
this.baseRimId = baseRimId;
this.supportRimId = supportRimId;
this.manufacturer = manufacturer;
this.model = model;
this.pcrIndex = pcrIndex;
this.digestValue = digestValue;
this.eventType = eventType;
this.matchFail = matchFail;
this.patched = patched;
this.updated = updated;
this.contentBlob = Arrays.clone(contentBlob);
}
/**
* Helper method to update the attributes of this object.
* @param support the associated RIM.
* @param baseRimId the main id to update
*/
public void updateInfo(final SupportReferenceManifest support, final UUID baseRimId) {
if (support != null) {
setBaseRimId(baseRimId);
setManufacturer(support.getPlatformManufacturer());
setModel(support.getPlatformModel());
setUpdated(true);
if (support.isSwidPatch()) {
// come back to this later, how does this get
// identified to be patched
setPatched(true);
}
}
}
/**
* Returns a string of the classes fields.
* @return a string
*/
public String toString() {
return String.format("ReferenceDigestValue: {%s, %d, %s, %s, "
+ "matchFail - %b, updated - %b, patched - %b}",
model, pcrIndex, digestValue, eventType, matchFail, updated, patched);
}
}

View File

@ -2,6 +2,8 @@ package hirs.attestationca.persist.entity.userdefined.rim;
import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnore;
import hirs.attestationca.persist.entity.userdefined.ReferenceManifest; import hirs.attestationca.persist.entity.userdefined.ReferenceManifest;
import hirs.attestationca.persist.service.ReferenceManifestService;
import hirs.attestationca.persist.service.selector.ReferenceManifestSelector;
import hirs.utils.tpm.eventlog.TCGEventLog; import hirs.utils.tpm.eventlog.TCGEventLog;
import hirs.utils.tpm.eventlog.TpmPcrEvent; import hirs.utils.tpm.eventlog.TpmPcrEvent;
import jakarta.persistence.Column; import jakarta.persistence.Column;
@ -34,6 +36,78 @@ public class SupportReferenceManifest extends ReferenceManifest {
@Column @Column
private boolean processed = false; private boolean processed = false;
/**
* This class enables the retrieval of SupportReferenceManifest by their attributes.
*/
public static class Selector extends ReferenceManifestSelector<SupportReferenceManifest> {
/**
* Construct a new ReferenceManifestSelector that will
* use the given (@link ReferenceManifestService}
* to retrieve one or may SupportReferenceManifest.
*
* @param referenceManifestManager the reference manifest manager to be used to retrieve
* reference manifests.
*/
public Selector(final ReferenceManifestService referenceManifestManager) {
super(referenceManifestManager, SupportReferenceManifest.class);
}
/**
* Specify the platform manufacturer that rims must have to be considered
* as matching.
* @param manufacturer string for the manufacturer
* @return this instance
*/
public Selector byManufacturer(final String manufacturer) {
setFieldValue(PLATFORM_MANUFACTURER, manufacturer);
return this;
}
/**
* Specify the platform model that rims must have to be considered
* as matching.
* @param manufacturer string for the manufacturer
* @param model string for the model
* @return this instance
*/
public Selector byManufacturerModel(final String manufacturer, final String model) {
setFieldValue(PLATFORM_MANUFACTURER, manufacturer);
setFieldValue(PLATFORM_MODEL, model);
return this;
}
/**
* Specify the device name that rims must have to be considered
* as matching.
* @param deviceName string for the deviceName
* @return this instance
*/
public Selector byDeviceName(final String deviceName) {
setFieldValue("deviceName", deviceName);
return this;
}
/**
* Specify the file name that rims should have.
* @param fileName the name of the file associated with the rim
* @return this instance
*/
public Selector byFileName(final String fileName) {
setFieldValue(RIM_FILENAME_FIELD, fileName);
return this;
}
/**
* Specify the RIM hash associated with the support RIM.
* @param hexDecHash the hash of the file associated with the rim
* @return this instance
*/
public Selector byHexDecHash(final String hexDecHash) {
setFieldValue(HEX_DEC_HASH_FIELD, hexDecHash);
return this;
}
}
/** /**
* Main constructor for the RIM object. This takes in a byte array of a * Main constructor for the RIM object. This takes in a byte array of a
* valid swidtag file and parses the information. * valid swidtag file and parses the information.
@ -69,6 +143,16 @@ public class SupportReferenceManifest extends ReferenceManifest {
this.pcrHash = 0; this.pcrHash = 0;
} }
/**
* Get a Selector for use in retrieving ReferenceManifest.
*
* @param rimMan the ReferenceManifestService to be used to retrieve
* persisted RIMs
* @return a Selector instance to use for retrieving RIMs
*/
public static Selector select(final ReferenceManifestService rimMan) {
return new Selector(rimMan);
}
/** /**
* Getter method for the expected PCR values contained within the support * Getter method for the expected PCR values contained within the support
@ -111,4 +195,13 @@ public class SupportReferenceManifest extends ReferenceManifest {
return new ArrayList<>(); return new ArrayList<>();
} }
/**
* This is a method to indicate whether or not this support
* rim is a base log file.
* @return flag for base.
*/
public boolean isBaseSupport() {
return !this.isSwidSupplemental() && !this.isSwidPatch();
}
} }

View File

@ -1,8 +1,13 @@
package hirs.attestationca.persist.enums; package hirs.attestationca.persist.enums;
import lombok.Getter;
import lombok.Setter;
/** /**
* Class to capture appraisal results and corresponding messages. * Class to capture appraisal results and corresponding messages.
*/ */
@Getter
@Setter
public class AppraisalStatus { public class AppraisalStatus {
/** /**
* Enum used to represent appraisal status. * Enum used to represent appraisal status.
@ -56,51 +61,4 @@ public class AppraisalStatus {
this.additionalInfo = additionalInfo; this.additionalInfo = additionalInfo;
} }
/**
* Get appraisal status.
* @return appraisal status
*/
public Status getAppStatus() {
return appStatus;
}
/**
* Set appraisal status.
* @param appStatus new status
*/
public void setAppStatus(final Status appStatus) {
this.appStatus = appStatus;
}
/**
* Get appraisal description message.
* @return appraisal description message
*/
public String getMessage() {
return message;
}
/**
* Set appraisal description message.
* @param message appraisal description message
*/
public void setMessage(final String message) {
this.message = message;
}
/**
* Getter for additional information during validation.
* @return string of additional information
*/
public String getAdditionalInfo() {
return additionalInfo;
}
/**
* Setter for any additional information.
* @param additionalInfo the string of additional information
*/
public void setAdditionalInfo(final String additionalInfo) {
this.additionalInfo = additionalInfo;
}
} }

View File

@ -0,0 +1,23 @@
package hirs.attestationca.persist.service;
import hirs.attestationca.persist.entity.userdefined.Certificate;
import hirs.attestationca.persist.service.selector.CertificateSelector;
import java.util.List;
import java.util.Set;
import java.util.UUID;
public interface CertificateService<T extends Certificate> {
Certificate saveCertificate(Certificate certificate);
<T extends Certificate> List<T> fetchCertificates(Class<T> classType);
Certificate updateCertificate(Certificate certificate, UUID certificateId);
Certificate updateCertificate(Certificate certificate);
void deleteCertificate(Certificate certificate);
<T extends Certificate> Set<T> get(CertificateSelector certificateSelector);
}

View File

@ -2,19 +2,84 @@ package hirs.attestationca.persist.service;
import hirs.attestationca.persist.entity.manager.CertificateRepository; import hirs.attestationca.persist.entity.manager.CertificateRepository;
import hirs.attestationca.persist.entity.userdefined.Certificate; import hirs.attestationca.persist.entity.userdefined.Certificate;
import hirs.attestationca.persist.service.selector.CertificateSelector;
import jakarta.persistence.EntityManager; import jakarta.persistence.EntityManager;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Set;
import java.util.UUID;
@Service @Service
public class CertificateServiceImpl { public class CertificateServiceImpl<T extends Certificate> extends DefaultDbService<Certificate> implements CertificateService<Certificate> {
@Autowired(required = false) @Autowired(required = false)
private EntityManager entityManager; private EntityManager entityManager;
@Autowired @Autowired
private CertificateRepository repository; private CertificateRepository repository;
private void saveCertificate(Certificate certificate) { @Override
repository.save(certificate); public Certificate saveCertificate(Certificate certificate) {
return repository.save(certificate);
}
@Override
@SuppressWarnings("unchecked")
public <T extends Certificate> List<T> fetchCertificates(Class<T> classType) {
return (List<T>) repository.findAll(Sort.sort(classType));
}
@Override
public Certificate updateCertificate(Certificate certificate, UUID certificateId) {
return saveCertificate(certificate);
}
@Override
public Certificate updateCertificate(Certificate certificate) {
return saveCertificate(certificate);
}
/**
* This method does not need to be used directly as it is used by {@link CertificateSelector}'s
* get* methods. Regardless, it may be used to retrieve certificates by other code in this
* package, given a configured CertificateSelector.
*
* Example:
*
* <pre>
* {@code
* CertificateSelector certSelector =
* new CertificateSelector(Certificate.Type.CERTIFICATE_AUTHORITY)
* .byIssuer("CN=Some certain issuer");
*
* Set<Certificate> certificates = certificateManager.get(certSelector);}
* </pre>
*
* @param <T> the type of certificate that will be retrieved
* @param certificateSelector a configured {@link CertificateSelector} to use for querying
* @return the resulting set of Certificates, possibly empty
*/
@SuppressWarnings("unchecked")
public <T extends Certificate> Set<T> get(final CertificateSelector certificateSelector) {
// return new HashSet<>(
// (List<T>) getWithCriteria(
// certificateSelector.getCertificateClass(),
// Collections.singleton(certificateSelector.getCriterion())
// )
// );
return null;
}
/**
* Remove a certificate from the database.
*
* @param certificate the certificate to delete
* @return true if deletion was successful, false otherwise
*/
public void deleteCertificate(final Certificate certificate) {
repository.delete(certificate);
} }
} }

View File

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

View File

@ -0,0 +1,199 @@
package hirs.attestationca.persist.service;
import hirs.attestationca.persist.DBManagerException;
import hirs.attestationca.persist.entity.ArchivableEntity;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import lombok.NoArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.hibernate.StaleObjectStateException;
import org.hibernate.exception.LockAcquisitionException;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.retry.RetryCallback;
import org.springframework.retry.RetryContext;
import org.springframework.retry.RetryListener;
import org.springframework.retry.backoff.FixedBackOffPolicy;
import org.springframework.retry.policy.SimpleRetryPolicy;
import org.springframework.retry.support.RetryTemplate;
import org.springframework.stereotype.Service;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
@Log4j2
@Service
@NoArgsConstructor
public class DefaultDbService<T extends ArchivableEntity> extends HibernateDbService<T> {
/**
* 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;
private static final int MAX_CLASS_CACHE_ENTRIES = 500;
private Class<T> clazz;
@PersistenceContext
private EntityManager entityManager;
private JpaRepository repository;
// structure for retrying methods in the database
private RetryTemplate retryTemplate;
/**
* Creates a new <code>DefaultDbService</code>.
*
* @param clazz Class to search for when doing Hibernate queries,
* unfortunately class type of T cannot be determined using only T
*/
public DefaultDbService(final Class<T> clazz) {
super(clazz, null);
setRetryTemplate();
}
/**
* Set the parameters used to retry database transactions. The retry template will
* retry transactions that throw a LockAcquisitionException or StaleObjectStateException.
*/
public final void setRetryTemplate() {
Map<Class<? extends Throwable>, Boolean> exceptionsToRetry = new HashMap<>();
exceptionsToRetry.put(LockAcquisitionException.class, true);
exceptionsToRetry.put(StaleObjectStateException.class, true);
SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy(
DEFAULT_MAX_RETRY_ATTEMPTS,
exceptionsToRetry,
true,
false
);
FixedBackOffPolicy backoffPolicy = new FixedBackOffPolicy();
backoffPolicy.setBackOffPeriod(DEFAULT_RETRY_WAIT_TIME_MS);
this.retryTemplate = new RetryTemplate();
this.retryTemplate.setRetryPolicy(retryPolicy);
this.retryTemplate.setBackOffPolicy(backoffPolicy);
}
/**
* Registers a retry listener to be notified of retry activity.
* @param retryListener the retry listener
*/
public void addRetryListener(final RetryListener retryListener) {
retryTemplate.registerListener(retryListener);
}
/**
* Retrieves the <code>Object</code> from the database. This searches the
* database for an entry whose name matches <code>name</code>. It then
* reconstructs the <code>Object</code> from the database entry.
*
* @param name name of the object
* @return object if found, otherwise null.
* @throws DBManagerException if unable to search the database or recreate
* the <code>Object</code>
*/
public final T get(final String name) throws DBManagerException {
return retryTemplate.execute(new RetryCallback<T, DBManagerException>() {
@Override
public T doWithRetry(final RetryContext context) throws DBManagerException {
return doGet(name);
}
});
}
/**
* Retrieves the <code>Object</code> from the database. This searches the
* database for an entry whose id matches <code>id</code>. It then
* reconstructs the <code>Object</code> from the database entry.
*
* @param id id of the object
* @return object if found, otherwise null.
* @throws DBManagerException if unable to search the database or recreate
* the <code>Object</code>
*/
public final T get(final Serializable id) throws DBManagerException {
return retryTemplate.execute(new RetryCallback<T, DBManagerException>() {
@Override
public T doWithRetry(final RetryContext context) throws DBManagerException {
return doGet(id);
}
});
}
/**
* Retrieves the <code>Object</code> from the database. This searches the
* database for an entry whose name matches <code>name</code>. It then
* reconstructs the <code>Object</code> from the database entry.
*
* @param name name of the object
* @return object if found, otherwise null.
* @throws DBManagerException if unable to search the database or recreate
* the <code>Object</code>
*/
protected T doGet(final String name) throws DBManagerException {
log.debug("getting object: {}", name);
if (name == null) {
log.debug("null name argument");
return null;
}
Object entity = entityManager.find(clazz, name);
entityManager.detach(entity);
return clazz.cast(entity);
}
/**
* Retrieves the <code>Object</code> from the database. This searches the
* database for an entry whose id matches <code>id</code>. It then
* reconstructs the <code>Object</code> from the database entry.
*
* @param id id of the object
* @return object if found, otherwise null.
* @throws DBManagerException if unable to search the database or recreate
* the <code>Object</code>
*/
protected T doGet(final Serializable id) throws DBManagerException {
log.debug("getting object: {}", id);
if (id == null) {
log.debug("null id argument");
return null;
}
Object entity = entityManager.find(clazz, id);
entityManager.detach(entity);
return clazz.cast(entity);
}
/**
* Archives the named object and updates it in the database.
*
* @param name name of the object to archive
* @return true if the object was successfully found and archived, false if the object was not
* found
* @throws DBManagerException if the object is not an instance of <code>ArchivableEntity</code>
*/
// @Override
// public final boolean archive(final String name) throws DBManagerException {
// log.debug("archiving object: {}", name);
// if (name == null) {
// log.debug("null name argument");
// return false;
// }
//
// T target = get(name);
// if (target == null) {
// return false;
// }
// if (!(target instanceof ArchivableEntity)) {
// throw new DBManagerException("unable to archive non-archivable object");
// }
//
// ((ArchivableEntity) target).archive();
// repository.save(target);
// return true;
// }
}

View File

@ -0,0 +1,132 @@
package hirs.attestationca.persist.service;
import hirs.attestationca.persist.DBManagerException;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.CriteriaQuery;
import lombok.extern.log4j.Log4j2;
import java.util.ArrayList;
import java.util.List;
/**
* Abstract class that has the underlying Hibernate commands used by other DB Managers.
* This class exists primarily to reduce code in {@link hirs.attestationca.persist.service.DefaultDbService} which retries these methods
* using a RetryTemplate.
*
* @param <T> type of objects to manage by this manager
*/
@Log4j2
public abstract class HibernateDbService<T> {
private static final int MAX_CLASS_CACHE_ENTRIES = 500;
private final Class<T> clazz;
@PersistenceContext
private EntityManager entityManager;
private CriteriaBuilder criteriaBuilder;
private CriteriaQuery<T> criteriaQuery;
/**
* Creates a new <code>AbstractDbManager</code>.
*
* @param clazz Class to search for when doing Hibernate queries,
* unfortunately class type of T cannot be determined using only T
* @param entityManager the session factory to use to interact with the database
*/
public HibernateDbService(final Class<T> clazz, final EntityManager entityManager) {
if (clazz == null) {
log.error("HibernateDbService cannot be instantiated with a null class");
throw new IllegalArgumentException(
"HibernateDbService cannot be instantiated with a null class"
);
}
// if (entityManager == null) {
// log.error("HibernateDbService cannot be instantiated with a null SessionFactory");
// throw new IllegalArgumentException(
// "HibernateDbService cannot be instantiated with a null SessionFactory"
// );
// }
this.clazz = clazz;
this.entityManager = entityManager;
}
public HibernateDbService() {
clazz = null;
}
/**
* Returns a list of all <code>T</code>s of type <code>clazz</code> in the database, with an
* additional restriction also specified in the query.
* <p>
* This would be useful if <code>T</code> has several subclasses being
* managed. This class argument allows the caller to limit which types of
* <code>T</code> should be returned.
*
* @param clazz class type of <code>T</code>s to search for (may be null to
* use Class&lt;T&gt;)
* @param additionalRestriction - an added Criterion to use in the query, null for none
* @return list of <code>T</code> names
* @throws DBManagerException if unable to search the database
*/
protected List<T> doGetList(final Class<? extends T> clazz)
throws DBManagerException {
log.debug("Getting object list");
Class<? extends T> searchClass = clazz;
if (clazz == null) {
log.debug("clazz is null");
searchClass = this.clazz;
}
List<T> objects = new ArrayList<>();
return objects;
}
/**
* Deletes the object from the database. This removes all of the database
* entries that stored information with regards to the this object.
* <p>
* If the object is referenced by any other tables then this will throw a
* <code>DBManagerException</code>.
*
* @param name name of the object to delete
* @return true if successfully found and deleted the object
* @throws DBManagerException if unable to find the baseline or delete it
* from the database
*/
// protected boolean doDelete(final String name) throws DBManagerException {
// log.debug("deleting object: {}", name);
// if (name == null) {
// log.debug("null name argument");
// return false;
// }
//
// boolean deleted = false;
// Session session = entityManager.unwrap(Session.class);
// try {
// log.debug("retrieving object from db");
// criteriaBuilder = session.getCriteriaBuilder();
// criteriaQuery = criteriaBuilder.createQuery(clazz);
// Root<T> root = criteriaQuery.from(clazz);
// criteriaQuery.select(root).where(criteriaBuilder.equal(root.get("name"), name));
//
// Object object = session.createQuery(criteriaQuery).getSingleResult();
//
// if (clazz.isInstance(object)) {
// T objectOfTypeT = clazz.cast(object);
// log.debug("found object, deleting it");
// session.delete(objectOfTypeT);
// deleted = true;
// }
// } catch (Exception e) {
// final String msg = "unable to retrieve object";
// log.error(msg, e);
// throw new DBManagerException(msg, e);
// }
// return deleted;
// }
}

View File

@ -0,0 +1,20 @@
package hirs.attestationca.persist.service;
import hirs.attestationca.persist.entity.userdefined.ReferenceManifest;
import hirs.attestationca.persist.entity.userdefined.rim.ReferenceDigestValue;
import java.util.List;
import java.util.UUID;
public interface ReferenceDigestValueService {
ReferenceDigestValue saveReferenceDigestValue(ReferenceDigestValue referenceDigestValue);
List<ReferenceDigestValue> fetchDigestValues();
ReferenceDigestValue updateRefDigestValue(ReferenceDigestValue referenceDigestValue, UUID rdvId);
List<ReferenceDigestValue> getValuesByRimId(ReferenceManifest baseRim);
void deleteRefDigestValueById(UUID rdvId);
}

View File

@ -0,0 +1,64 @@
package hirs.attestationca.persist.service;
import hirs.attestationca.persist.entity.manager.ReferenceDigestValueRepository;
import hirs.attestationca.persist.entity.userdefined.ReferenceManifest;
import hirs.attestationca.persist.entity.userdefined.rim.ReferenceDigestValue;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.jpa.datatables.mapping.DataTablesInput;
import org.springframework.data.jpa.datatables.mapping.DataTablesOutput;
import org.springframework.stereotype.Service;
import java.util.LinkedList;
import java.util.List;
import java.util.UUID;
@Service
public class ReferenceDigestValueServiceImpl extends DefaultDbService<ReferenceDigestValue> implements ReferenceDigestValueService {
@Autowired
private ReferenceDigestValueRepository repository;
@Override
public ReferenceDigestValue saveReferenceDigestValue(ReferenceDigestValue referenceDigestValue) {
return repository.save(referenceDigestValue);
}
public List<ReferenceDigestValue> findAll() {
return repository.findAll();
}
@Override
public List<ReferenceDigestValue> fetchDigestValues() {
return repository.findAll();
}
@Override
public ReferenceDigestValue updateRefDigestValue(ReferenceDigestValue referenceDigestValue, UUID rdvId) {
return saveReferenceDigestValue(referenceDigestValue);
}
public ReferenceDigestValue updateRefDigestValue(ReferenceDigestValue referenceDigestValue) {
if (referenceDigestValue.getId() != null) {
return updateRefDigestValue(referenceDigestValue, referenceDigestValue.getId());
}
return null;
}
public List<ReferenceDigestValue> getValuesByRimId(ReferenceManifest baseRim) {
List<ReferenceDigestValue> results = new LinkedList<>();
if (baseRim != null) {
for (ReferenceDigestValue rdv : repository.findAll()) {
if (rdv.getBaseRimId() == baseRim.getId()) {
results.add(rdv);
}
}
}
return results;
}
@Override
public void deleteRefDigestValueById(UUID rdvId) {
repository.getReferenceById(rdvId).archive();
}
}

View File

@ -0,0 +1,23 @@
package hirs.attestationca.persist.service;
import hirs.attestationca.persist.OrderedListQuerier;
import hirs.attestationca.persist.entity.userdefined.ReferenceManifest;
import hirs.attestationca.persist.service.selector.ReferenceManifestSelector;
import java.util.List;
import java.util.Set;
import java.util.UUID;
public interface ReferenceManifestService<T extends ReferenceManifest> extends OrderedListQuerier<T> {
ReferenceManifest saveReferenceManifest(ReferenceManifest referenceManifest);
List<ReferenceManifest> fetchReferenceManifests();
// DataTablesOutput<ReferenceManifest> fetchReferenceManifests(DataTablesInput input);
ReferenceManifest updateReferenceManifest(ReferenceManifest referenceManifest, UUID rimId);
void deleteReferenceManifestById(UUID rimId);
<T extends ReferenceManifest> Set<T> get(ReferenceManifestSelector referenceManifestSelector);
}

View File

@ -1,10 +1,15 @@
package hirs.attestationca.persist.service; package hirs.attestationca.persist.service;
import hirs.attestationca.persist.CriteriaModifier;
import hirs.attestationca.persist.DBManagerException;
import hirs.attestationca.persist.FilteredRecordsList;
import hirs.attestationca.persist.entity.manager.ReferenceManifestRepository; import hirs.attestationca.persist.entity.manager.ReferenceManifestRepository;
import hirs.attestationca.persist.entity.userdefined.ReferenceManifest; import hirs.attestationca.persist.entity.userdefined.ReferenceManifest;
import hirs.attestationca.persist.service.selector.ReferenceManifestSelector;
import jakarta.persistence.EntityManager; import jakarta.persistence.EntityManager;
import lombok.extern.log4j.Log4j2; import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.xml.sax.SAXException; import org.xml.sax.SAXException;
@ -13,10 +18,14 @@ import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory; import javax.xml.validation.SchemaFactory;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
@Log4j2 @Log4j2
@Service @Service
public class ReferenceManifestServiceImpl { public class ReferenceManifestServiceImpl<T extends ReferenceManifest> extends DefaultDbService<ReferenceManifest> implements ReferenceManifestService<ReferenceManifest> {
/** /**
* The variable that establishes a schema factory for xml processing. * The variable that establishes a schema factory for xml processing.
@ -67,4 +76,69 @@ public class ReferenceManifestServiceImpl {
} }
return schema; return schema;
} }
@Override
public ReferenceManifest saveReferenceManifest(ReferenceManifest referenceManifest) {
return repository.save(referenceManifest);
}
@Override
public List<ReferenceManifest> fetchReferenceManifests() {
return repository.findAll();
}
/**
* This method does not need to be used directly as it is used by
* {@link ReferenceManifestSelector}'s get* methods. Regardless, it may be
* used to retrieve ReferenceManifest by other code in this package, given a
* configured ReferenceManifestSelector.
*
* @param referenceManifestSelector a configured
* {@link ReferenceManifestSelector} to use for querying
* @return the resulting set of ReferenceManifest, possibly empty
*/
@SuppressWarnings("unchecked")
public <T extends ReferenceManifest> List<T> get(
Class<T> classType) {
log.info("Getting the full set of Reference Manifest files.");
// return new HashSet<>(
// (List<T>) getWithCriteria(
// referenceManifestSelector.getReferenceManifestClass(),
// Collections.singleton(referenceManifestSelector.getCriterion())
// )
// );
return (List<T>) repository.findAll(Sort.sort(classType));
}
@Override
public ReferenceManifest updateReferenceManifest(ReferenceManifest referenceManifest, UUID rimId) {
return null;
}
@Override
public void deleteReferenceManifestById(UUID rimId) {
repository.deleteById(rimId);
}
@Override
public <T extends ReferenceManifest> Set<T> get(ReferenceManifestSelector referenceManifestSelector) {
return null;
}
@Override
public FilteredRecordsList getOrderedList(Class<? extends ReferenceManifest> clazz,
String columnToOrder, boolean ascending, int firstResult,
int maxResults, String search,
Map<String, Boolean> searchableColumns) throws DBManagerException {
return null;
}
@Override
public FilteredRecordsList<ReferenceManifest> getOrderedList(Class<? extends ReferenceManifest> clazz,
String columnToOrder, boolean ascending,
int firstResult, int maxResults, String search,
Map<String, Boolean> searchableColumns,
CriteriaModifier<ReferenceManifest> criteriaModifier) throws DBManagerException {
return null;
}
} }

View File

@ -0,0 +1,16 @@
package hirs.attestationca.persist.service;
import hirs.attestationca.persist.entity.userdefined.SupplyChainValidation;
import java.util.List;
import java.util.UUID;
public interface SupplyChainValidationService {
SupplyChainValidation saveSupplyChainValidation(SupplyChainValidation supplyChainValidation);
List<SupplyChainValidation> fetchSupplyChainValidations();
SupplyChainValidation updateSupplyChainValidation(SupplyChainValidation supplyChainValidation, UUID scvId);
void deleteSupplyChainValidation(UUID scvId);
}

View File

@ -0,0 +1,190 @@
package hirs.attestationca.persist.service;
import hirs.attestationca.persist.entity.manager.SupplyChainValidationRepository;
import hirs.attestationca.persist.entity.userdefined.Certificate;
import hirs.attestationca.persist.entity.userdefined.SupplyChainValidation;
import hirs.attestationca.persist.entity.userdefined.certificate.CertificateAuthorityCredential;
import hirs.attestationca.persist.entity.userdefined.certificate.PlatformCredential;
import hirs.utils.BouncyCastleUtils;
import lombok.extern.log4j.Log4j2;
import org.bouncycastle.util.encoders.Hex;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.io.IOException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
@Log4j2
@Service
public class SupplyChainValidationServiceImpl extends DefaultDbService<SupplyChainValidation> implements SupplyChainValidationService {
@Autowired
SupplyChainValidationRepository repository;
@Autowired
private CertificateService certificateService;
public SupplyChainValidationServiceImpl(final CertificateService certificateService) {
super();
this.certificateService = certificateService;
}
@Override
public SupplyChainValidation saveSupplyChainValidation(SupplyChainValidation supplyChainValidation) {
return repository.save(supplyChainValidation);
}
@Override
public List<SupplyChainValidation> fetchSupplyChainValidations() {
return repository.findAll();
}
@Override
public SupplyChainValidation updateSupplyChainValidation(SupplyChainValidation supplyChainValidation, UUID scvId) {
return null;
}
@Override
public void deleteSupplyChainValidation(UUID scvId) {
repository.deleteById(scvId);
}
/**
* This method is used to retrieve the entire CA chain (up to a trusted
* self-signed certificate) for the given certificate. This method will look
* up CA certificates that have a matching issuer organization as the given
* certificate, and will perform that operation recursively until all
* certificates for all relevant organizations have been retrieved. For that
* reason, the returned set of certificates may be larger than the the
* single trust chain for the queried certificate, but is guaranteed to
* include the trust chain if it exists in this class' CertificateManager.
* Returns the certificate authority credentials in a KeyStore.
*
* @param credential the credential whose CA chain should be retrieved
* @return A keystore containing all relevant CA credentials to the given
* certificate's organization or null if the keystore can't be assembled
*/
public KeyStore getCaChain(final Certificate credential) {
KeyStore caKeyStore = null;
try {
caKeyStore = caCertSetToKeystore(getCaChainRec(credential, Collections.emptySet()));
} catch (KeyStoreException | IOException e) {
log.error("Unable to assemble CA keystore", e);
}
return caKeyStore;
}
/**
* This is a recursive method which is used to retrieve the entire CA chain
* (up to a trusted self-signed certificate) for the given certificate. This
* method will look up CA certificates that have a matching issuer
* organization as the given certificate, and will perform that operation
* recursively until all certificates for all relevant organizations have
* been retrieved. For that reason, the returned set of certificates may be
* larger than the the single trust chain for the queried certificate, but
* is guaranteed to include the trust chain if it exists in this class'
* CertificateManager.
* <p>
* Implementation notes: 1. Queries for CA certs with a subject org matching
* the given (argument's) issuer org 2. Add that org to
* queriedOrganizations, so we don't search for that organization again 3.
* For each returned CA cert, add that cert to the result set, and recurse
* with that as the argument (to go up the chain), if and only if we haven't
* already queried for that organization (which prevents infinite loops on
* certs with an identical subject and issuer org)
*
* @param credential the credential whose CA chain should be retrieved
* @param previouslyQueriedSubjects a list of organizations to refrain
* from querying
* @return a Set containing all relevant CA credentials to the given
* certificate's organization
*/
private Set<CertificateAuthorityCredential> getCaChainRec(
final Certificate credential,
final Set<String> previouslyQueriedSubjects) {
CertificateAuthorityCredential skiCA = null;
Set<CertificateAuthorityCredential> certAuthsWithMatchingIssuer = new HashSet<>();
if (credential.getAuthorityKeyIdentifier() != null
&& !credential.getAuthorityKeyIdentifier().isEmpty()) {
byte[] bytes = Hex.decode(credential.getAuthorityKeyIdentifier());
skiCA = CertificateAuthorityCredential
.select(certificateService)
.bySubjectKeyIdentifier(bytes).getCertificate();
}
if (skiCA == null) {
if (credential.getIssuerSorted() == null
|| credential.getIssuerSorted().isEmpty()) {
certAuthsWithMatchingIssuer = CertificateAuthorityCredential
.select(certificateService)
.bySubject(credential.getHolderIssuer())
.getCertificates();
} else {
//Get certificates by subject organization
certAuthsWithMatchingIssuer = CertificateAuthorityCredential
.select(certificateService)
.bySubjectSorted(credential.getIssuerSorted())
.getCertificates();
}
} else {
certAuthsWithMatchingIssuer.add(skiCA);
}
Set<String> queriedOrganizations = new HashSet<>(previouslyQueriedSubjects);
queriedOrganizations.add(credential.getHolderIssuer());
HashSet<CertificateAuthorityCredential> caCreds = new HashSet<>();
for (CertificateAuthorityCredential cred : certAuthsWithMatchingIssuer) {
caCreds.add(cred);
if (!BouncyCastleUtils.x500NameCompare(cred.getHolderIssuer(),
cred.getSubject())) {
caCreds.addAll(getCaChainRec(cred, queriedOrganizations));
}
}
return caCreds;
}
private KeyStore caCertSetToKeystore(final Set<CertificateAuthorityCredential> certs)
throws KeyStoreException, IOException {
KeyStore keyStore = KeyStore.getInstance("JKS");
try {
keyStore.load(null, "".toCharArray());
for (Certificate cert : certs) {
keyStore.setCertificateEntry(cert.getId().toString(), cert.getX509Certificate());
}
} catch (IOException | CertificateException | NoSuchAlgorithmException e) {
throw new IOException("Could not create and populate keystore", e);
}
return keyStore;
}
private boolean checkForMultipleBaseCredentials(final String platformSerialNumber) {
boolean multiple = false;
PlatformCredential baseCredential = null;
if (platformSerialNumber != null) {
List<PlatformCredential> chainCertificates = PlatformCredential
.select(certificateService)
.byBoardSerialNumber(platformSerialNumber)
.getCertificates().stream().collect(Collectors.toList());
for (PlatformCredential pc : chainCertificates) {
if (baseCredential != null && pc.isPlatformBase()) {
multiple = true;
} else if (pc.isPlatformBase()) {
baseCredential = pc;
}
}
}
return multiple;
}
}

View File

@ -0,0 +1,473 @@
package hirs.attestationca.persist.service.selector;
import com.google.common.base.Preconditions;
import hirs.attestationca.persist.entity.userdefined.Certificate;
import hirs.attestationca.persist.service.CertificateService;
import hirs.attestationca.persist.service.CertificateServiceImpl;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.CriteriaQuery;
import jakarta.persistence.criteria.Predicate;
import jakarta.persistence.criteria.Root;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.bouncycastle.util.Arrays;
import java.io.IOException;
import java.math.BigInteger;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
/**
* This class is used to select one or many certificates in conjunction
* with a {@link CertificateServiceImpl}. To make use of this object,
* use (some CertificateImpl).select(CertificateManager).
*
* This class loosely follows the builder pattern. It is instantiated with
* the type of certificate that should be retrieved. It is possible to
* further specify which certificate(s) should be retrieved by using an
* instance's by* methods; each call to a by* method will further
* restrict the result set. At any time, the results may be retrieved
* by using one of the get* methods according to the form the
* results should be in.
*
* If no matching certificates were found for the query, the returned
* value may empty or null, depending on the return type.
*
* For example, to retrieve all platform certificates:
*
* <pre>
* {@code
* Set<Certificate> certificates =
* certificateManager.select(Certificate.Type.PLATFORM)
* .getCertificates();
* }
* </pre>
*
* To retrieve all CA certificates in a KeyStore:
*
* <pre>
* {@code
* KeyStore trustStore =
* certificateManager.select(Certificate.Type.CERTIFICATE_AUTHORITY)
* .getKeyStore();
* }
* </pre>
*
* To retrieve all CA certificates matching a certain issuer in X509 format:
*
* <pre>
* {@code
* Set<X509Certificate> certificates =
* certificateManager.select(Certificate.Type.CERTIFICATE_AUTHORITY)
* .byIssuer("CN=Some certain issuer")
* .getX509Certificates();
* }
* </pre>
*
* @param <T> the type of certificate that will be retrieved
*/
public abstract class CertificateSelector<T extends Certificate> {
private final CertificateService certificateManager;
private final Class<T> certificateClass;
private final Map<String, Object> fieldValueSelections;
private boolean excludeArchivedCertificates;
/**
* Construct a new CertificateSelector that will use the given {@link CertificateServiceImpl} to
* retrieve certificates of the given type.
*
* @param certificateManager the certificate manager to be used to retrieve certificates
* @param certificateClass the class of certificate to be retrieved
*/
public CertificateSelector(
final CertificateService certificateManager,
final Class<T> certificateClass) {
this(certificateManager, certificateClass, true);
}
/**
* Construct a new CertificateSelector that will use the given {@link CertificateService} to
* retrieve certificates of the given type.
*
* @param certificateManager the certificate manager to be used to retrieve certificates
* @param certificateClass the class of certificate to be retrieved
* @param excludeArchivedCertificates true if excluding archived certificates
*/
public CertificateSelector(
final CertificateService certificateManager,
final Class<T> certificateClass, final boolean excludeArchivedCertificates) {
Preconditions.checkArgument(
certificateManager != null,
"certificate manager cannot be null"
);
Preconditions.checkArgument(
certificateClass != null,
"type cannot be null"
);
this.certificateManager = certificateManager;
this.certificateClass = certificateClass;
this.fieldValueSelections = new HashMap<>();
this.excludeArchivedCertificates = excludeArchivedCertificates;
}
/**
* Specify the entity id that certificates must have to be considered
* as matching.
*
* @param uuid the UUID to query
* @return this instance (for chaining further calls)
*/
public CertificateSelector<T> byEntityId(final UUID uuid) {
setFieldValue(Certificate.ID_FIELD, uuid);
return this;
}
/**
* Specify the hash code of the bytes that certificates must match.
*
* @param certificateHash the hash code of the bytes to query for
* @return this instance (for chaining further calls)
*/
public CertificateSelector<T> byHashCode(final int certificateHash) {
setFieldValue(Certificate.CERTIFICATE_HASH_FIELD, certificateHash);
return this;
}
/**
* Specify a serial number that certificates must have to be considered
* as matching.
*
* @param serialNumber the serial number to query
* @return this instance (for chaining further calls)
*/
public CertificateSelector<T> bySerialNumber(final BigInteger serialNumber) {
setFieldValue(Certificate.SERIAL_NUMBER_FIELD, serialNumber);
return this;
}
/**
* Specify a holder serial number that certificates must have to be considered
* as matching.
*
* @param holderSerialNumber the holder serial number to query
* @return this instance (for chaining further calls)
*/
public CertificateSelector<T> byHolderSerialNumber(final BigInteger holderSerialNumber) {
setFieldValue(Certificate.HOLDER_SERIAL_NUMBER_FIELD, holderSerialNumber);
return this;
}
/**
* Specify an issuer string that certificates must have to be considered
* as matching.
*
* @param issuer certificate issuer string to query, not empty or null
* @return this instance (for chaining further calls)
*/
public CertificateSelector<T> byIssuer(final String issuer) {
Preconditions.checkArgument(
StringUtils.isNotEmpty(issuer),
String.format("%s: issuer cannot be null or empty.",
this.certificateClass.toString())
);
setFieldValue(Certificate.ISSUER_FIELD, issuer);
return this;
}
/**
* Specify a subject string that certificates must have to be considered
* as matching.
*
* @param subject certificate subject string to query, not empty or null
* @return this instance (for chaining further calls)
*/
public CertificateSelector<T> bySubject(final String subject) {
Preconditions.checkArgument(
StringUtils.isNotEmpty(subject),
String.format("%s: subject cannot be null or empty.",
this.certificateClass.toString())
);
setFieldValue(Certificate.SUBJECT_FIELD, subject);
return this;
}
/**
* Specify the sorted issuer string that certificates must have to be considered
* as matching.
*
* @param issuerSorted certificate issuer organization string to query, not empty or null
* @return this instance (for chaining further calls)
*/
public CertificateSelector<T> byIssuerSorted(final String issuerSorted) {
Preconditions.checkArgument(
StringUtils.isNotEmpty(issuerSorted),
String.format("%s: issuerSorted cannot be null or empty.",
this.certificateClass.toString())
);
setFieldValue(Certificate.ISSUER_SORTED_FIELD, issuerSorted);
return this;
}
/**
* Specify the sorted subject string that certificates must have to be considered
* as matching.
*
* @param subjectSorted certificate subject organization string to query, not empty or null
* @return this instance (for chaining further calls)
*/
public CertificateSelector<T> bySubjectSorted(final String subjectSorted) {
Preconditions.checkArgument(
StringUtils.isNotEmpty(subjectSorted),
String.format("%s: subjectSorted cannot be null or empty.",
this.certificateClass.toString())
);
setFieldValue(Certificate.SUBJECT_SORTED_FIELD, subjectSorted);
return this;
}
/**
* Specify a public key that certificates must have to be considered
* as matching.
*
* @param encodedPublicKey the binary-encoded public key to query, not empty or null
* @return this instance (for chaining further calls)
*/
public CertificateSelector<T> byEncodedPublicKey(final byte[] encodedPublicKey) {
Preconditions.checkArgument(
ArrayUtils.isNotEmpty(encodedPublicKey),
String.format("%s: publicKey cannot be null or empty.",
this.certificateClass.toString())
);
setFieldValue(
Certificate.ENCODED_PUBLIC_KEY_FIELD,
Arrays.copyOf(encodedPublicKey, encodedPublicKey.length)
);
return this;
}
/**
* Specify the authority key identifier to find certificate(s).
* @param authorityKeyIdentifier the string of the AKI associated with the certificate.
* @return this instance
*/
public CertificateSelector<T> byAuthorityKeyIdentifier(final String authorityKeyIdentifier) {
Preconditions.checkArgument(
StringUtils.isNotEmpty(authorityKeyIdentifier),
String.format("%s: authorityKeyIdentifier cannot be null or empty.",
this.certificateClass.toString())
);
setFieldValue(Certificate.AUTHORITY_KEY_ID_FIELD, authorityKeyIdentifier);
return this;
}
/**
* Specify a public key modulus that certificates must have to be considered
* as matching.
*
* @param publicKeyModulus a BigInteger representing a public key's modulus to query not null
* @return this instance (for chaining further calls)
*/
public CertificateSelector<T> byPublicKeyModulus(final BigInteger publicKeyModulus) {
Preconditions.checkArgument(
publicKeyModulus != null,
String.format("%s: Public key modulus cannot be null",
this.certificateClass.toString())
);
setFieldValue(
Certificate.PUBLIC_KEY_MODULUS_FIELD,
publicKeyModulus.toString(Certificate.HEX_BASE)
);
return this;
}
/**
* Set a field name and value to match.
*
* @param name the field name to query
* @param value the value to query
*/
protected void setFieldValue(final String name, final Object value) {
Object valueToAssign = value;
Preconditions.checkArgument(
value != null,
String.format("field value (%s) cannot be null.", name)
);
if (value instanceof String) {
Preconditions.checkArgument(
StringUtils.isNotEmpty((String) value),
"field value cannot be empty."
);
}
if (value instanceof byte[]) {
byte[] valueBytes = (byte[]) value;
Preconditions.checkArgument(
ArrayUtils.isNotEmpty(valueBytes),
String.format("field value (%s) cannot be empty.", name)
);
valueToAssign = Arrays.copyOf(valueBytes, valueBytes.length);
}
fieldValueSelections.put(name, valueToAssign);
}
/**
* Retrieve the result set as a single {@link Certificate}.
* This method is best used when selecting on a unique attribute.
* If the result set contains more than one certificate, one is chosen
* arbitrarily and returned. If no matching certificates are found,
* this method returns null.
*
* @return a matching certificate or null if none is found
*/
public T getCertificate() {
Set<T> certs = execute();
if (certs.size() == 0) {
return null;
}
return certs.iterator().next();
}
/**
* Retrieve the result set as a set of {@link Certificate}s.
* This method is best used when selecting on non-unique attributes.
* Certificates are populated into the set in no specific order.
* If no matching certificates are found, the returned Set will be empty.
*
* @return a Set of matching Certificates, possibly empty
*/
public Set<T> getCertificates() {
return Collections.unmodifiableSet(new HashSet<>(execute()));
}
/**
* Retrieve the result set as a single {@link X509Certificate}.
* This method is best used when selecting on a unique attribute.
* If the result set contains more than one certificate, one is chosen
* arbitrarily and returned. If no matching certificates are found,
* this method returns null.
*
* @return a matching certificate or null if none is found
* @throws IOException if there is a problem reconstructing the X509Certificate
*/
public X509Certificate getX509Certificate() throws IOException {
Certificate cert = getCertificate();
if (cert == null) {
return null;
}
return cert.getX509Certificate();
}
/**
* Retrieve the result set as a set of {@link X509Certificate}s.
* This method is best used when selecting on non-unique attributes.
* Certificates are populated into the set in no specific order.
* If no matching certificates are found, the returned Set will be empty.
*
* @return a Set of matching Certificates, possibly empty
* @throws IOException if there is a problem reconstructing the X509Certificates
*/
public Set<X509Certificate> getX509Certificates() throws IOException {
Set<X509Certificate> certs = new HashSet<>();
for (Certificate cert : getCertificates()) {
certs.add(cert.getX509Certificate());
}
return Collections.unmodifiableSet(certs);
}
/**
* Retrieve the result set populated into a {@link KeyStore}.
* Certificates are populated into a JKS-formatted KeyStore, with their aliases
* set to their unique identifiers.
* If no matching certificates are found, the returned KeyStore will be empty.
*
* @return a KeyStore populated with the matching certificates, if any
* @throws KeyStoreException if there is a problem instantiating a JKS-formatted KeyStore
* @throws IOException if there is a problem populating the keystore
*/
public KeyStore getKeyStore() throws KeyStoreException, IOException {
KeyStore keyStore = KeyStore.getInstance("JKS");
try {
keyStore.load(null, "".toCharArray());
for (Certificate cert : getCertificates()) {
keyStore.setCertificateEntry(cert.getId().toString(), cert.getX509Certificate());
}
} catch (IOException | CertificateException | NoSuchAlgorithmException e) {
throw new IOException("Could not create and populate keystore", e);
}
return keyStore;
}
/**
* Construct the criterion that can be used to query for certificates matching the configuration
* of this {@link CertificateSelector}.
*
* @return a Criterion that can be used to query for certificates matching the configuration of
* this instance
*/
Predicate[] getCriterion(final CriteriaBuilder criteriaBuilder) {
Predicate[] predicates = new Predicate[fieldValueSelections.size()];
CriteriaQuery<T> query = criteriaBuilder.createQuery(getCertificateClass());
Root<T> root = query.from(getCertificateClass());
int i = 0;
for (Map.Entry<String, Object> fieldValueEntry : fieldValueSelections.entrySet()) {
predicates[i++] = criteriaBuilder.equal(root.get(fieldValueEntry.getKey()), fieldValueEntry.getValue());
}
if (this.excludeArchivedCertificates) {
predicates[i] = criteriaBuilder.isNull(root.get(Certificate.ARCHIVE_FIELD));
}
return predicates;
}
/**
* @return the certificate class that this instance will query
*/
public Class<T> getCertificateClass() {
return certificateClass;
}
// construct and execute query
private Set<T> execute() {
return certificateManager.get(this);
}
/**
* Configures the selector to query for archived and unarchived certificates.
* @return the selector
*/
public CertificateSelector<T> includeArchived() {
excludeArchivedCertificates = false;
return this;
}
}

View File

@ -0,0 +1,233 @@
package hirs.attestationca.persist.service.selector;
import com.google.common.base.Preconditions;
import hirs.attestationca.persist.entity.userdefined.Certificate;
import hirs.attestationca.persist.entity.userdefined.ReferenceManifest;
import hirs.attestationca.persist.service.ReferenceManifestService;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.CriteriaQuery;
import jakarta.persistence.criteria.Predicate;
import jakarta.persistence.criteria.Root;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
/**
* This class is used to select one or many RIMs in conjunction
* with a {@link ReferenceManifestService}. To make use of this object,
* use (some ReferenceManifest).select(ReferenceManifestManager).
*
* @param <T> the type of Reference Integrity Manifest that will be retrieved.
*/
public abstract class ReferenceManifestSelector<T extends ReferenceManifest> {
/**
* String representing the database field for the manufacturer.
*/
public static final String PLATFORM_MANUFACTURER = "platformManufacturer";
/**
* String representing the database field for the manufacturer id.
*/
public static final String PLATFORM_MANUFACTURER_ID = "platformManufacturerId";
/**
* String representing the database field for the model.
*/
public static final String PLATFORM_MODEL = "platformModel";
/**
* String representing the database field for the filename.
*/
public static final String RIM_FILENAME_FIELD = "fileName";
private static final String RIM_TYPE_FIELD = "rimType";
private final ReferenceManifestService referenceManifestManager;
private final Class<T> referenceTypeClass;
private final Map<String, Object> fieldValueSelections;
private boolean excludeArchivedRims;
/**
* Default Constructor.
*
* @param referenceManifestManager the RIM manager to be used to retrieve RIMs
* @param referenceTypeClass the type of Reference Manifest to process.
*/
public ReferenceManifestSelector(final ReferenceManifestService referenceManifestManager,
final Class<T> referenceTypeClass) {
this(referenceManifestManager, referenceTypeClass, true);
}
/**
* Standard Constructor for the Selector.
*
* @param referenceManifestManager the RIM manager to be used to retrieve RIMs
* @param referenceTypeClass the type of Reference Manifest to process.
* @param excludeArchivedRims true if excluding archived RIMs
*/
public ReferenceManifestSelector(final ReferenceManifestService referenceManifestManager,
final Class<T> referenceTypeClass,
final boolean excludeArchivedRims) {
Preconditions.checkArgument(
referenceManifestManager != null,
"reference manifest manager cannot be null"
);
Preconditions.checkArgument(
referenceTypeClass != null,
"type cannot be null"
);
this.referenceManifestManager = referenceManifestManager;
this.referenceTypeClass = referenceTypeClass;
this.excludeArchivedRims = excludeArchivedRims;
this.fieldValueSelections = new HashMap<>();
}
/**
* Specify the entity id that rims must have to be considered as matching.
*
* @param uuid the UUID to query
* @return this instance (for chaining further calls)
*/
public ReferenceManifestSelector<T> byEntityId(final UUID uuid) {
setFieldValue(Certificate.ID_FIELD, uuid);
return this;
}
/**
* Specify the file name of the object to grab.
* @param fileName the name of the file associated with the rim
* @return instance of the manifest in relation to the filename.
*/
public ReferenceManifestSelector<T> byFileName(final String fileName) {
setFieldValue(RIM_FILENAME_FIELD, fileName);
return this;
}
/**
* Specify the RIM Type to match.
* @param rimType the type of rim
* @return this instance
*/
public ReferenceManifestSelector<T> byRimType(final String rimType) {
setFieldValue(RIM_TYPE_FIELD, rimType);
return this;
}
/**
* Set a field name and value to match.
*
* @param name the field name to query
* @param value the value to query
*/
protected void setFieldValue(final String name, final Object value) {
Object valueToAssign = value;
Preconditions.checkArgument(
value != null,
String.format("field value (%s) cannot be null.", name)
);
if (value instanceof String) {
Preconditions.checkArgument(
StringUtils.isNotEmpty((String) value),
"field value cannot be empty."
);
}
if (value instanceof byte[]) {
byte[] valueBytes = (byte[]) value;
Preconditions.checkArgument(
ArrayUtils.isNotEmpty(valueBytes),
String.format("field value (%s) cannot be empty.", name)
);
valueToAssign = Arrays.copyOf(valueBytes, valueBytes.length);
}
fieldValueSelections.put(name, valueToAssign);
}
/**
* Retrieve the result set as a single
* {@link ReferenceManifest}. This method is best used
* when selecting on a unique attribute. If the result set contains more
* than one RIM, one is chosen arbitrarily and returned. If no matching RIMs
* are found, this method returns null.
*
* @return a matching RIM or null if none is found
*/
public T getRIM() {
Set<T> rims = execute();
if (rims.isEmpty()) {
return null;
}
return rims.iterator().next();
}
/**
* Retrieve the result set as a set of
* {@link ReferenceManifest}s. This method is best used
* when selecting on non-unique attributes. ReferenceManifests are populated
* into the set in no specific order. If no matching certificates are found,
* the returned Set will be empty.
*
* @return a Set of matching RIMs, possibly empty
*/
public Set<T> getRIMs() {
return Set.copyOf(execute());
}
/**
* Construct the criterion that can be used to query for rims matching the
* configuration of this {@link ReferenceManifestSelector}.
*
* @return a Criterion that can be used to query for rims matching the
* configuration of this instance
*/
Predicate[] getCriterion(final CriteriaBuilder criteriaBuilder) {
Predicate[] predicates = new Predicate[fieldValueSelections.size()];
CriteriaQuery<T> query = criteriaBuilder.createQuery(getReferenceManifestClass());
Root<T> root = query.from(getReferenceManifestClass());
int i = 0;
for (Map.Entry<String, Object> fieldValueEntry : fieldValueSelections.entrySet()) {
predicates[i++] = criteriaBuilder.equal(root.get(fieldValueEntry.getKey()), fieldValueEntry.getValue());
}
if (this.excludeArchivedRims) {
predicates[i] = criteriaBuilder.isNull(root.get(Certificate.ARCHIVE_FIELD));
}
return predicates;
}
/**
* @return the rim class that this instance will query
*/
public Class<T> getReferenceManifestClass() {
return this.referenceTypeClass;
}
// construct and execute query
private Set<T> execute() {
Set<T> results = this.referenceManifestManager.get(this);
return results;
}
/**
* Configures the selector to query for archived and unarchived rims.
*
* @return the selector
*/
public ReferenceManifestSelector<T> includeArchived() {
this.excludeArchivedRims = false;
return this;
}
}

View File

@ -0,0 +1,68 @@
package hirs.attestationca.persist.validation;
import hirs.attestationca.persist.entity.userdefined.SupplyChainValidation;
import hirs.attestationca.persist.entity.userdefined.certificate.EndorsementCredential;
import hirs.attestationca.persist.entity.userdefined.certificate.PlatformCredential;
import hirs.attestationca.persist.entity.userdefined.report.DeviceInfoReport;
import hirs.attestationca.persist.enums.AppraisalStatus;
import java.security.KeyStore;
import java.util.Map;
/**
* A class used to support supply chain validation by performing the actual
* validation of credentials.
*/
public interface CredentialValidator {
/**
* Checks if the platform credential is valid.
*
* @param pc The platform credential to verify.
* @param trustStore trust store holding trusted certificates.
* @param acceptExpired whether or not to accept expired certificates as valid.
* @return The result of the validation.
*/
AppraisalStatus validatePlatformCredential(PlatformCredential pc,
KeyStore trustStore,
boolean acceptExpired);
/**
* Checks if the platform credential's attributes are valid.
* @param pc The platform credential to verify.
* @param deviceInfoReport Report containing the serial numbers of the platform to be validated.
* @param ec The endorsement credential supplied from the same identity request as
* the platform credential.
* @return The result of the validation.
*/
AppraisalStatus validatePlatformCredentialAttributes(PlatformCredential pc,
DeviceInfoReport deviceInfoReport,
EndorsementCredential ec);
/**
* Checks if the delta credential's attributes are valid.
* @param delta the delta credential to verify
* @param deviceInfoReport The device info report containing
* serial number of the platform to be validated.
* @param base the base credential from the same identity request
* as the delta credential.
* @param deltaMapping delta certificates associated with the
* delta supply validation.
* @return the result of the validation.
*/
AppraisalStatus validateDeltaPlatformCredentialAttributes(PlatformCredential delta,
DeviceInfoReport deviceInfoReport,
PlatformCredential base,
Map<PlatformCredential,
SupplyChainValidation> deltaMapping);
/**
* Checks if the endorsement credential is valid.
*
* @param ec the endorsement credential to verify.
* @param trustStore trust store holding trusted trusted certificates.
* @param acceptExpired whether or not to accept expired certificates as valid.
* @return the result of the validation.
*/
AppraisalStatus validateEndorsementCredential(EndorsementCredential ec,
KeyStore trustStore,
boolean acceptExpired);
}

View File

@ -0,0 +1,445 @@
package hirs.attestationca.persist.validation;
import hirs.attestationca.persist.entity.userdefined.ReferenceManifest;
import hirs.attestationca.persist.entity.userdefined.certificate.CertificateAuthorityCredential;
import jakarta.xml.bind.JAXBContext;
import jakarta.xml.bind.JAXBException;
import jakarta.xml.bind.UnmarshalException;
import jakarta.xml.bind.Unmarshaller;
import lombok.extern.log4j.Log4j2;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import javax.xml.XMLConstants;
import javax.xml.crypto.AlgorithmMethod;
import javax.xml.crypto.KeySelector;
import javax.xml.crypto.KeySelectorException;
import javax.xml.crypto.KeySelectorResult;
import javax.xml.crypto.MarshalException;
import javax.xml.crypto.XMLCryptoContext;
import javax.xml.crypto.XMLStructure;
import javax.xml.crypto.dsig.XMLSignature;
import javax.xml.crypto.dsig.XMLSignatureException;
import javax.xml.crypto.dsig.XMLSignatureFactory;
import javax.xml.crypto.dsig.dom.DOMValidateContext;
import javax.xml.crypto.dsig.keyinfo.KeyInfo;
import javax.xml.crypto.dsig.keyinfo.X509Data;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.security.Key;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.Iterator;
/**
* This class handles validation functions of RIM files.
* Currently supports validation of support RIM hashes and
* base RIM signatures.
*/
@Log4j2
public class ReferenceManifestValidator {
private static final String SIGNATURE_ALGORITHM_RSA_SHA256 =
"http://www.w3.org/2001/04/xmldsig-more#rsa-sha256";
private static final String SCHEMA_PACKAGE = "hirs.utils.xjc";
private static final String SCHEMA_URL = "swid_schema.xsd";
private static final String SCHEMA_LANGUAGE = XMLConstants.W3C_XML_SCHEMA_NS_URI;
private static final String IDENTITY_TRANSFORM = "identity_transform.xslt";
private static final String SHA256 = "SHA-256";
private static final int EIGHT_BIT_MASK = 0xff;
private static final int LEFT_SHIFT = 0x100;
private static final int RADIX = 16;
private Document rim;
private Unmarshaller unmarshaller;
private PublicKey publicKey;
private Schema schema;
private String subjectKeyIdentifier;
private boolean signatureValid, supportRimValid;
/**
* Setter for the RIM to be validated. The ReferenceManifest object is converted into a
* Document for processing.
*
* @param rim ReferenceManifest object
*/
public void setRim(final ReferenceManifest rim) {
try {
Document doc = validateSwidtagSchema(removeXMLWhitespace(new StreamSource(
new ByteArrayInputStream(rim.getRimBytes()))));
this.rim = doc;
} catch (IOException e) {
log.error("Error while unmarshalling rim bytes: " + e.getMessage());
}
}
/**
* Getter for signatureValid.
*
* @return true if valid, false if not.
*/
public boolean isSignatureValid() {
return signatureValid;
}
/**
* Getter for supportRimValid.
*
* @return true if valid, false if not.
*/
public boolean isSupportRimValid() {
return supportRimValid;
}
/**
* Getter for certificate PublicKey.
*
* @return PublicKey
*/
public PublicKey getPublicKey() {
return publicKey;
}
/**
* Getter for subjectKeyIdentifier.
*
* @return subjectKeyIdentifier
*/
public String getSubjectKeyIdentifier() {
return subjectKeyIdentifier;
}
/**
* This default constructor creates the Schema object from SCHEMA_URL immediately to save
* time during validation calls later.
*/
public ReferenceManifestValidator() {
try {
InputStream is = ReferenceManifestValidator.class
.getClassLoader().getResourceAsStream(SCHEMA_URL);
SchemaFactory schemaFactory = SchemaFactory.newInstance(SCHEMA_LANGUAGE);
schema = schemaFactory.newSchema(new StreamSource(is));
rim = null;
signatureValid = false;
supportRimValid = false;
publicKey = null;
subjectKeyIdentifier = "(not found)";
} catch (SAXException e) {
log.warn("Error setting schema for validation!");
}
}
/**
* This method attempts to validate the signature element of the instance's RIM
* using a given cert. The cert is compared to either the RIM's embedded certificate
* or the RIM's subject key identifier. If the cert is matched then validation proceeds,
* otherwise validation ends.
*
* @param cert the cert to be checked against the RIM
* @return true if the signature element is validated, false otherwise
*/
@SuppressWarnings("magicnumber")
public boolean validateXmlSignature(final CertificateAuthorityCredential cert) {
DOMValidateContext context = null;
try {
NodeList nodes = rim.getElementsByTagNameNS(XMLSignature.XMLNS, "Signature");
if (nodes.getLength() == 0) {
log.error("Cannot validate RIM, signature element not found!");
return false;
}
NodeList certElement = rim.getElementsByTagName("X509Certificate");
if (certElement.getLength() > 0) {
X509Certificate embeddedCert = parseCertFromPEMString(
certElement.item(0).getTextContent());
if (embeddedCert != null) {
subjectKeyIdentifier = getCertificateSubjectKeyIdentifier(embeddedCert);
if (Arrays.equals(embeddedCert.getPublicKey().getEncoded(),
cert.getEncodedPublicKey())) {
context = new DOMValidateContext(new X509KeySelector(), nodes.item(0));
}
}
} else {
subjectKeyIdentifier = getKeyName(rim);
if (subjectKeyIdentifier.equals(cert.getSubjectKeyIdString())) {
context = new DOMValidateContext(cert.getX509Certificate().getPublicKey(),
nodes.item(0));
}
}
if (context != null) {
publicKey = cert.getX509Certificate().getPublicKey();
signatureValid = validateSignedXMLDocument(context);
return signatureValid;
}
} catch (IOException e) {
log.warn("Error while parsing certificate data: " + e.getMessage());
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
/**
* This method calculates the SHA256 hash of the input byte array and compares it against
* the value passed in.
*
* @param input byte array to hash.
* @param expected value to compare against.
*/
public void validateSupportRimHash(final byte[] input, final String expected) {
String calculatedHash = getHashValue(input, SHA256);
supportRimValid = calculatedHash.equals(expected);
if (!supportRimValid) {
log.info("Unmatched support RIM hash! Expected: " + expected
+ ", actual: " + calculatedHash);
}
}
/**
* This method calculates the digest of a byte array based on the hashing algorithm passed in.
*
* @param input byte array.
* @param sha hash algorithm.
* @return String digest.
*/
private String getHashValue(final byte[] input, final String sha) {
String resultString = null;
try {
MessageDigest md = MessageDigest.getInstance(sha);
byte[] bytes = md.digest(input);
StringBuilder sb = new StringBuilder();
for (int i = 0; i < bytes.length; i++) {
sb.append(Integer.toString((bytes[i] & EIGHT_BIT_MASK)
+ LEFT_SHIFT, RADIX).substring(1));
}
resultString = sb.toString();
} catch (NoSuchAlgorithmException grex) {
log.warn(grex.getMessage());
}
return resultString;
}
private boolean validateSignedXMLDocument(final DOMValidateContext context) {
try {
XMLSignatureFactory sigFactory = XMLSignatureFactory.getInstance("DOM");
XMLSignature signature = sigFactory.unmarshalXMLSignature(context);
return signature.validate(context);
} catch (MarshalException e) {
log.warn("Error while unmarshalling XML signature: " + e.getMessage());
} catch (XMLSignatureException e) {
log.warn("Error while validating XML signature: " + e.getMessage());
}
return false;
}
/**
* This internal class handles selecting an X509 certificate embedded in a KeyInfo element.
* It is passed as a parameter to a DOMValidateContext that uses it to validate
* an XML signature.
*/
public static class X509KeySelector extends KeySelector {
/**
* This method selects a public key for validation.
* PKs are parsed preferentially from the following elements:
* - X509Data
* - KeyValue
* The parsed PK is then verified based on the provided algorithm before
* being returned in a KeySelectorResult.
*
* @param keyinfo object containing the cert.
* @param purpose purpose.
* @param algorithm algorithm.
* @param context XMLCryptoContext.
* @return KeySelectorResult holding the PublicKey.
* @throws KeySelectorException exception.
*/
public KeySelectorResult select(final KeyInfo keyinfo,
final KeySelector.Purpose purpose,
final AlgorithmMethod algorithm,
final XMLCryptoContext context)
throws KeySelectorException {
Iterator keyinfoItr = keyinfo.getContent().iterator();
while (keyinfoItr.hasNext()) {
XMLStructure element = (XMLStructure) keyinfoItr.next();
if (element instanceof X509Data) {
X509Data data = (X509Data) element;
Iterator dataItr = data.getContent().iterator();
while (dataItr.hasNext()) {
Object object = dataItr.next();
if (object instanceof X509Certificate) {
final PublicKey publicKey = ((X509Certificate) object).getPublicKey();
if (areAlgorithmsEqual(algorithm.getAlgorithm(),
publicKey.getAlgorithm())) {
return new ReferenceManifestValidator.X509KeySelector
.RIMKeySelectorResult(publicKey);
}
}
}
}
}
throw new KeySelectorException("No key found!");
}
/**
* This method checks if two strings refer to the same algorithm.
*
* @param uri string 1
* @param name string 2
* @return true if equal, false if not
*/
public boolean areAlgorithmsEqual(final String uri, final String name) {
return uri.equals(SIGNATURE_ALGORITHM_RSA_SHA256) && name.equalsIgnoreCase("RSA");
}
/**
* This internal class creates a KeySelectorResult from the public key.
*/
private static class RIMKeySelectorResult implements KeySelectorResult {
private Key key;
RIMKeySelectorResult(final Key key) {
this.key = key;
}
public Key getKey() {
return key;
}
}
}
/**
* This method extracts certificate bytes from a string. The bytes are assumed to be
* PEM format, and a header and footer are concatenated with the input string to
* facilitate proper parsing.
*
* @param pemString the input string
* @return an X509Certificate created from the string, or null
* @throws Exception if certificate cannot be successfully parsed
*/
private X509Certificate parseCertFromPEMString(final String pemString) throws Exception {
String certificateHeader = "-----BEGIN CERTIFICATE-----";
String certificateFooter = "-----END CERTIFICATE-----";
try {
CertificateFactory factory = CertificateFactory.getInstance("X.509");
InputStream inputStream = new ByteArrayInputStream((certificateHeader
+ System.lineSeparator()
+ pemString
+ System.lineSeparator()
+ certificateFooter).getBytes("UTF-8"));
return (X509Certificate) factory.generateCertificate(inputStream);
} catch (CertificateException e) {
log.warn("Error creating CertificateFactory instance: " + e.getMessage());
} catch (UnsupportedEncodingException e) {
log.warn("Error while parsing cert from PEM string: " + e.getMessage());
}
return null;
}
/**
* This method returns the subjectKeyIdentifier from a given X509Certificate.
*
* @param certificate the cert to pull the subjectKeyIdentifier from
* @return the String representation of the subjectKeyIdentifier
* @throws IOException
*/
private String getCertificateSubjectKeyIdentifier(final X509Certificate certificate)
throws IOException {
String decodedValue;
byte[] extension = certificate.getExtensionValue(Extension.subjectKeyIdentifier.getId());
if (extension != null && extension.length > 0) {
decodedValue = JcaX509ExtensionUtils.parseExtensionValue(extension).toString();
} else {
decodedValue = " "; //Unlikely that a proper X509Certificate does not have a skid
}
return decodedValue.substring(1); //Drop the # at the beginning of the string
}
/**
* This method parses the subject key identifier from the KeyName element of a signature.
*
* @param doc
* @return SKID if found, or an empty string.
*/
private String getKeyName(final Document doc) {
NodeList keyName = doc.getElementsByTagName("KeyName");
if (keyName.getLength() > 0) {
return keyName.item(0).getTextContent();
} else {
return null;
}
}
/**
* This method validates the Document against the schema.
*
* @param doc of the input swidtag.
* @return document validated against the schema.
*/
private Document validateSwidtagSchema(final Document doc) {
try {
JAXBContext jaxbContext = JAXBContext.newInstance(SCHEMA_PACKAGE);
unmarshaller = jaxbContext.createUnmarshaller();
unmarshaller.setSchema(schema);
unmarshaller.unmarshal(doc);
} catch (UnmarshalException e) {
log.warn("Error validating swidtag file!");
} catch (IllegalArgumentException e) {
log.warn("Input file empty.");
} catch (JAXBException e) {
e.printStackTrace();
}
return doc;
}
/**
* This method strips all whitespace from an xml file, including indents and spaces
* added for human-readability.
*
* @param source of the input xml.
* @return Document representation of the xml.
*/
private Document removeXMLWhitespace(final StreamSource source) throws IOException {
TransformerFactory tf = TransformerFactory.newInstance();
Source identitySource = new StreamSource(
ReferenceManifestValidator.class.getClassLoader()
.getResourceAsStream(IDENTITY_TRANSFORM));
Document doc = null;
try {
Transformer transformer = tf.newTransformer(identitySource);
DOMResult result = new DOMResult();
transformer.transform(source, result);
doc = (Document) result.getNode();
} catch (TransformerConfigurationException e) {
log.warn("Error configuring transformer!");
e.printStackTrace();
} catch (TransformerException e) {
log.warn("Error transforming input!");
e.printStackTrace();
}
return doc;
}
}

View File

@ -0,0 +1,45 @@
package hirs.attestationca.persist.validation;
/**
* This class represents exceptions thrown by the SupplyChainValidator class.
*/
public class SupplyChainValidatorException extends Exception {
private static final long serialVersionUID = 8563981058518865230L;
/**
* Creates a new <code>SupplyChainValidatorException</code> that has the message
* <code>message</code> and <code>Throwable</code> cause <code>cause</code>.
*
* @param message
* exception message
* @param cause
* root cause
*/
public SupplyChainValidatorException(final String message, final Throwable cause) {
super(message, cause);
}
/**
* Creates a new <code>SupplyChainValidatorException</code> that has the <code>String</code>
* message <code>message</code>.
*
* @param message
* exception message
*/
public SupplyChainValidatorException(final String message) {
super(message);
}
/**
* Creates a new <code>SupplyChainValidatorException</code> that has the <code>Throwable</code>
* cause <code>cause</code>.
*
* @param cause
* root cause
*/
public SupplyChainValidatorException(final Throwable cause) {
super(cause);
}
}

View File

@ -13,47 +13,4 @@ public class HIRSDbInitializer implements ServletContextListener {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
@Autowired @Autowired
static SettingsServiceImpl settingsService = new SettingsServiceImpl(); 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

@ -1,5 +1,7 @@
package hirs.attestationca.portal; package hirs.attestationca.portal;
import hirs.attestationca.persist.entity.userdefined.SupplyChainSettings;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.ComponentScan;
@ -17,15 +19,29 @@ import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement; import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.sql.DataSource; import javax.sql.DataSource;
import java.security.cert.X509Certificate;
import java.util.Properties; import java.util.Properties;
@Log4j2
@Configuration @Configuration
@EnableTransactionManagement @EnableTransactionManagement
@PropertySource({ "classpath:hibernate.properties" }) @PropertySource({ "classpath:hibernate.properties", "classpath:portal.properties" })
@ComponentScan({ "hirs.attestationca.portal.page" }) @ComponentScan({ "hirs.attestationca.portal.page.controllers", "hirs.attestationca.persist.entity" })
@EnableJpaRepositories(basePackages = "hirs.attestationca.persist") @EnableJpaRepositories(basePackages = "hirs.attestationca.persist.entity.manager")
public class PersistenceJPAConfig { public class PersistenceJPAConfig {
@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 @Autowired
private Environment environment; private Environment environment;
@ -53,6 +69,97 @@ public class PersistenceJPAConfig {
return dataSource; return dataSource;
} }
/**
* 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 ioEx) {
// throw new BeanInitializationException(
// "Encountered error while initializing ACA directories: " + ioEx.getMessage(), ioEx);
// }
//
// // 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 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 ksEx) {
// throw new BeanInitializationException("Encountered error loading ACA certificate "
// + "from key store: " + ksEx.getMessage(), ksEx);
// }
// }
/**
* @return the {@link java.security.KeyStore} that contains the certificates
* for the ACA.
*/
// @Bean
// public KeyStore keyStore() {
// Path keyStorePath = Paths.get(keyStoreLocation);
//
// // creating empty store
// String storePassword = "storePassword";
// String storeName = "emptyStore.jks";
// String storeType = "jks";
//
// // 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());
//
// // empty
// try (FileOutputStream fileOutputStream = new FileOutputStream(storeName)) {
// KeyStore keyStore = KeyStore.getInstance(storeType);
// keyStore.load(null, storePassword.toCharArray());
//// keyStore.setCertificateEntry(keyAlias,);
// keyStore.store(fileOutputStream, storePassword.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);
// }
// }
@Bean @Bean
public PlatformTransactionManager transactionManager() { public PlatformTransactionManager transactionManager() {
final JpaTransactionManager transactionManager = new JpaTransactionManager(); final JpaTransactionManager transactionManager = new JpaTransactionManager();
@ -76,11 +183,11 @@ public class PersistenceJPAConfig {
return hibernateProperties; return hibernateProperties;
} }
//
// @Bean(name="default-settings") @Bean(name="default-settings")
// public SupplyChainSettings supplyChainSettings() { public SupplyChainSettings supplyChainSettings() {
// SupplyChainSettings scSettings = new SupplyChainSettings("Default", "Settings are configured for no validation flags set."); SupplyChainSettings scSettings = new SupplyChainSettings("Default", "Settings are configured for no validation flags set.");
//
// return scSettings; return scSettings;
// } }
} }

View File

@ -0,0 +1,77 @@
package hirs.attestationca.portal.datatables;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
/**
* Java representation of a jQuery DataTables Column.
*/
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor(access = AccessLevel.PUBLIC)
public class Column {
/**
* 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;
/**
* 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

@ -0,0 +1,224 @@
package hirs.attestationca.portal.datatables;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.CollectionUtils;
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.
*/
@NoArgsConstructor(access = AccessLevel.PUBLIC)
public class DataTableInput {
private static final int DEFAULT_LENGTH = 10;
/**
* 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)
@Getter
@Setter
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)
@Getter
@Setter
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)
@Getter
@Setter
private int length = DEFAULT_LENGTH;
/**
* Global search parameter.
*/
@Getter
@Setter
@NotNull
private Search search = new Search();
/**
* Order parameter.
*/
@Getter
@NotEmpty
private List<Order> order = new ArrayList<>();
/**
* Per-column search parameter.
*/
@Getter
@NotEmpty
private List<Column> columns = new ArrayList<>();
/**
* Sets the orders.
* @param order the orders
*/
public void setOrder(final List<Order> order) {
this.order.clear();
this.order.addAll(order);
}
/**
* 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,5 +1,6 @@
package hirs.attestationca.portal.datatables; package hirs.attestationca.portal.datatables;
import hirs.attestationca.persist.FilteredRecordsList;
import lombok.AccessLevel; import lombok.AccessLevel;
import lombok.Getter; import lombok.Getter;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
@ -30,11 +31,11 @@ public final class DataTableResponse<T> {
* @param recordList the filtered record list * @param recordList the filtered record list
* @param inputQuery the data table input (used for draw) * @param inputQuery the data table input (used for draw)
*/ */
// public DataTableResponse(final FilteredRecordsList<T> recordList, public DataTableResponse(final FilteredRecordsList<T> recordList,
// final DataTableInput inputQuery) { final DataTableInput inputQuery) {
// this(recordList, inputQuery.getDraw(), this(recordList, inputQuery.getDraw(),
// recordList.getRecordsTotal(), recordList.getRecordsFiltered()); recordList.getRecordsTotal(), recordList.getRecordsFiltered());
// } }
/** /**
* Constructs a data table response using the specified data with the data table specific * Constructs a data table response using the specified data with the data table specific

View File

@ -0,0 +1,72 @@
package hirs.attestationca.portal.datatables;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Pattern;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
/**
* Represents a column ordering with regards to a jQuery DataTable.
*/
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor(access = AccessLevel.PUBLIC)
public class Order {
/**
* 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;
/**
*
* @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

@ -0,0 +1,72 @@
package hirs.attestationca.portal.datatables;
import hirs.attestationca.persist.CriteriaModifier;
import hirs.attestationca.persist.FilteredRecordsList;
import hirs.attestationca.persist.OrderedListQuerier;
import org.springframework.util.CollectionUtils;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 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) {
return getOrderedList(clazz, dbManager, dataTableInput, orderColumnName, null);
}
/**
* 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

@ -0,0 +1,50 @@
package hirs.attestationca.portal.datatables;
import jakarta.validation.constraints.NotNull;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
/**
* Represents a jQuery DataTables search parameter.
*/
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor(access = AccessLevel.PUBLIC)
public class Search {
/**
* Constructor for a non-regex search.
* @param value the search value
*/
public Search(final String value) {
this(value, false);
}
/**
* 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;
@Override
public String toString() {
return "Search{"
+ "value='" + value + '\''
+ ", regex=" + regex
+ '}';
}
}

View File

@ -1,16 +1,47 @@
package hirs.attestationca.portal.page.controllers; package hirs.attestationca.portal.page.controllers;
import hirs.attestationca.persist.DBServiceException;
import hirs.attestationca.persist.entity.userdefined.Certificate;
import hirs.attestationca.persist.entity.userdefined.certificate.CertificateAuthorityCredential; import hirs.attestationca.persist.entity.userdefined.certificate.CertificateAuthorityCredential;
import hirs.attestationca.persist.entity.userdefined.certificate.EndorsementCredential;
import hirs.attestationca.persist.entity.userdefined.certificate.IssuedAttestationCertificate;
import hirs.attestationca.persist.entity.userdefined.certificate.PlatformCredential;
import hirs.attestationca.persist.service.CertificateService;
import hirs.attestationca.persist.service.CertificateServiceImpl; import hirs.attestationca.persist.service.CertificateServiceImpl;
import hirs.attestationca.portal.page.Page; import hirs.attestationca.portal.page.Page;
import hirs.attestationca.portal.page.PageController; import hirs.attestationca.portal.page.PageController;
import hirs.attestationca.portal.page.PageMessages;
import hirs.attestationca.portal.page.params.NoPageParams; import hirs.attestationca.portal.page.params.NoPageParams;
import hirs.attestationca.portal.page.utils.CertificateStringMapBuilder;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.log4j.Log4j2; import lombok.extern.log4j.Log4j2;
import org.bouncycastle.util.encoders.DecoderException;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
import org.springframework.ui.Model; 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.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.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.security.cert.CertificateEncodingException;
//import java.security.cert.X509Certificate;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
// note uploading base64 certs, old or new having decode issues check ACA channel
@Log4j2 @Log4j2
@Controller @Controller
@ -34,12 +65,12 @@ public class CertificatePageController extends PageController<NoPageParams> {
* Constructor providing the Page's display and routing specification. * Constructor providing the Page's display and routing specification.
* *
* @param certificateServiceImpl the certificate manager * @param certificateServiceImpl the certificate manager
* @param crudManager the CRUD manager for certificates // * @param crudManager the CRUD manager for certificates
* @param acaCertificate the ACA's X509 certificate // * @param acaCertificate the ACA's X509 certificate
*/ */
@Autowired @Autowired
public CertificatePageController( public CertificatePageController(
final CertificateServiceImpl certificateServiceImpl final CertificateServiceImpl certificateServiceImpl//,
// final CrudManager<Certificate> crudManager, // final CrudManager<Certificate> crudManager,
// final X509Certificate acaCertificate // final X509Certificate acaCertificate
) { ) {
@ -48,12 +79,12 @@ public class CertificatePageController extends PageController<NoPageParams> {
// this.dataTableQuerier = crudManager; // this.dataTableQuerier = crudManager;
// try { // try {
//// certificateAuthorityCredential // certificateAuthorityCredential
//// = new CertificateAuthorityCredential(acaCertificate.getEncoded()); // = new CertificateAuthorityCredential(acaCertificate.getEncoded());
// } catch (IOException e) { // } catch (IOException ioEx) {
// log.error("Failed to read ACA certificate", e); // log.error("Failed to read ACA certificate", ioEx);
// } catch (CertificateEncodingException e) { // } catch (CertificateEncodingException ceEx) {
// log.error("Error getting encoded ACA certificate", e); // log.error("Error getting encoded ACA certificate", ceEx);
// } // }
} }
@ -70,4 +101,524 @@ public class CertificatePageController extends PageController<NoPageParams> {
public ModelAndView initPage(final NoPageParams params, final Model model) { public ModelAndView initPage(final NoPageParams params, final Model model) {
return getBaseModelAndView(); 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.certificateServiceImpl));
mav.addObject(ACA_CERT_DATA, data);
break;
default:
// send to an error page
break;
}
return mav;
}
/**
* 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,
certificateServiceImpl);
}
}
//Add messages to the model
model.put(MESSAGES_ATTRIBUTE, messages);
return redirectTo(getCertificatePage(certificateType), new NoPageParams(), model, attr);
}
/**
* 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 {
log.info("Handling request to download all trust chain certificates");
String fileName = "trust-chain.zip";
final String singleFileName = "ca-certificates";
// 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
bulkDownload(zipOut, this.certificateServiceImpl.fetchCertificates(CertificateAuthorityCredential.class), singleFileName);
// write cert to output stream
} catch (IllegalArgumentException ex) {
String uuidError = "Failed to parse ID from: ";
log.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 {
log.info("Handling request to download all platform certificates");
String fileName = "platform_certificates.zip";
final String singleFileName = "Platform_Certificate";
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
bulkDownload(zipOut, this.certificateServiceImpl.fetchCertificates(PlatformCredential.class), singleFileName);
// write cert to output stream
} catch (IllegalArgumentException ex) {
String uuidError = "Failed to parse ID from: ";
log.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 {
log.info("Handling request to download all issued certificates");
String fileName = "issued_certificates.zip";
final String singleFileName = "Issued_Certificate";
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
bulkDownload(zipOut, this.certificateServiceImpl.fetchCertificates(IssuedAttestationCertificate.class), singleFileName);
// write cert to output stream
} catch (IllegalArgumentException ex) {
String uuidError = "Failed to parse ID from: ";
log.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 {
log.info("Handling request to download all endorsement certificates");
String fileName = "endorsement_certificates.zip";
final String singleFileName = "Endorsement_Certificates";
// 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
bulkDownload(zipOut, this.certificateServiceImpl.fetchCertificates(EndorsementCredential.class), singleFileName);
// write cert to output stream
} catch (IllegalArgumentException ex) {
String uuidError = "Failed to parse ID from: ";
log.error(uuidError, ex);
// send a 404 error when invalid certificate
response.sendError(HttpServletResponse.SC_NOT_FOUND);
}
}
private ZipOutputStream bulkDownload(final ZipOutputStream zipOut,
final List<Certificate> certificates,
final String singleFileName) throws IOException {
String zipFileName;
// get all files
for (Certificate certificate : certificates) {
zipFileName = String.format("%s[%s].cer", singleFileName,
Integer.toHexString(certificate.getCertificateHash()));
// configure the zip entry, the properties of the 'file'
ZipEntry zipEntry = new ZipEntry(zipFileName);
zipEntry.setSize((long) certificate.getRawBytes().length * Byte.SIZE);
zipEntry.setTime(System.currentTimeMillis());
zipOut.putNextEntry(zipEntry);
// the content of the resource
StreamUtils.copy(certificate.getRawBytes(), zipOut);
zipOut.closeEntry();
}
zipOut.finish();
return zipOut;
}
/**
* 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)
return switch (certificateType) {
case PLATFORMCREDENTIAL -> Page.PLATFORM_CREDENTIALS;
case ENDORSEMENTCREDENTIAL -> Page.ENDORSEMENT_KEY_CREDENTIALS;
case ISSUEDCERTIFICATES -> Page.ISSUED_CERTIFICATES;
default -> Page.TRUST_CHAIN;
};
}
/**
* 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 CertificateService 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 CertificateService 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) {
log.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);
log.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);
log.error(failMessage);
messages.addError(failMessage);
return null;
}
} catch (IOException e) {
final String failMessage = String.format(
"Failed to parse uploaded file (%s): ", fileName);
log.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);
log.error(failMessage, dEx);
messages.addError(failMessage + dEx.getMessage());
return null;
} catch (IllegalArgumentException e) {
final String failMessage = String.format(
"Certificate format not recognized(%s): ", fileName);
log.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 CertificateService certificateManager) {
Certificate existingCertificate;
// look for an identical certificate in the database
try {
existingCertificate = getCertificateByHash(
certificateType,
certificate.getCertificateHash(),
certificateManager);
} catch (DBServiceException e) {
final String failMessage = "Querying for existing certificate failed ("
+ fileName + "): ";
messages.addError(failMessage + e.getMessage());
log.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.isPlatformBase()) {
List<PlatformCredential> sharedCertificates = getCertificateByBoardSN(
certificateType,
platformCertificate.getPlatformSerial(),
certificateManager);
if (sharedCertificates != null) {
for (PlatformCredential pc : sharedCertificates) {
if (pc.isPlatformBase()) {
final String failMessage = "Storing certificate failed: "
+ "platform credential "
+ "chain (" + pc.getPlatformSerial()
+ ") base already exists in this chain ("
+ fileName + ")";
messages.addError(failMessage);
log.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.saveCertificate(certificate);
final String successMsg
= String.format("New certificate successfully uploaded (%s): ", fileName);
messages.addSuccess(successMsg);
log.info(successMsg);
return;
}
} catch (DBServiceException e) {
final String failMessage = String.format("Storing new certificate failed (%s): ",
fileName);
messages.addError(failMessage + e.getMessage());
log.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.updateCertificate(existingCertificate);
final String successMsg = String.format("Pre-existing certificate "
+ "found and unarchived (%s): ", fileName);
messages.addSuccess(successMsg);
log.info(successMsg);
return;
}
} catch (DBServiceException 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());
log.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);
log.error(failMessage);
}
} }

View File

@ -0,0 +1,601 @@
package hirs.attestationca.portal.page.controllers;
import hirs.attestationca.persist.DBServiceException;
import hirs.attestationca.persist.entity.userdefined.ReferenceManifest;
import hirs.attestationca.persist.entity.userdefined.certificate.CertificateAuthorityCredential;
import hirs.attestationca.persist.entity.userdefined.rim.BaseReferenceManifest;
import hirs.attestationca.persist.entity.userdefined.rim.EventLogMeasurements;
import hirs.attestationca.persist.entity.userdefined.rim.ReferenceDigestValue;
import hirs.attestationca.persist.entity.userdefined.rim.SupportReferenceManifest;
import hirs.attestationca.persist.service.CertificateService;
import hirs.attestationca.persist.service.ReferenceDigestValueService;
import hirs.attestationca.persist.service.ReferenceDigestValueServiceImpl;
import hirs.attestationca.persist.service.ReferenceManifestService;
import hirs.attestationca.persist.service.ReferenceManifestServiceImpl;
import hirs.attestationca.persist.service.SupplyChainValidationServiceImpl;
import hirs.attestationca.persist.validation.ReferenceManifestValidator;
import hirs.attestationca.persist.validation.SupplyChainValidatorException;
import hirs.attestationca.portal.page.Page;
import hirs.attestationca.portal.page.PageController;
import hirs.attestationca.portal.page.PageMessages;
import hirs.attestationca.portal.page.params.ReferenceManifestDetailsPageParams;
import hirs.attestationca.portal.page.utils.SupplyChainCredentialValidator;
import hirs.utils.SwidResource;
import hirs.utils.tpm.eventlog.TCGEventLog;
import hirs.utils.tpm.eventlog.TpmPcrEvent;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import java.io.IOException;
import java.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.
*/
@Log4j2
@Controller
@RequestMapping("/rim-details")
public class ReferenceManifestDetailsPageController extends PageController<ReferenceManifestDetailsPageParams> {
private final ReferenceManifestService referenceManifestManager;
private final ReferenceDigestValueService referenceEventManager;
private final CertificateService certificateService;
private static final ReferenceManifestValidator RIM_VALIDATOR
= new ReferenceManifestValidator();
/**
* Constructor providing the Page's display and routing specification.
*
* @param referenceManifestManager the reference manifest manager.
* @param referenceEventManager the reference event manager.
* @param certificateService the certificate manager.
*/
@Autowired
public ReferenceManifestDetailsPageController(
final ReferenceManifestServiceImpl referenceManifestManager,
final ReferenceDigestValueServiceImpl referenceEventManager,
final CertificateService certificateService) {
super(Page.RIM_DETAILS);
this.referenceManifestManager = referenceManifestManager;
this.referenceEventManager = referenceEventManager;
this.certificateService = certificateService;
}
/**
* 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);
log.debug(typeError);
mav.addObject(MESSAGES_ATTRIBUTE, messages);
} else {
try {
UUID uuid = UUID.fromString(params.getId());
data.putAll(getRimDetailInfo(uuid, referenceManifestManager,
referenceEventManager, certificateService));
} catch (IllegalArgumentException iaEx) {
String uuidError = "Failed to parse ID from: " + params.getId();
messages.addError(uuidError);
log.error(uuidError, iaEx);
} catch (Exception ioEx) {
log.error(ioEx);
}
if (data.isEmpty()) {
String notFoundMessage = "Unable to find RIM with ID: " + params.getId();
messages.addError(notFoundMessage);
log.warn(notFoundMessage);
mav.addObject(MESSAGES_ATTRIBUTE, messages);
} else {
mav.addObject(INITIAL_DATA, data);
}
}
// return the model and view
return mav;
}
/**
* 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 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 ReferenceManifestService referenceManifestManager,
final ReferenceDigestValueService referenceEventManager,
final CertificateService 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,
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 ReferenceManifestService referenceManifestManager,
final CertificateService 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.getSwidCorpus() == 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) {
log.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) {
log.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 ReferenceManifestService 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.updateReferenceManifest(support, support.getId());
} catch (DBServiceException ex) {
log.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 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 ReferenceManifestService referenceManifestManager,
final ReferenceDigestValueService 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

@ -0,0 +1,120 @@
package hirs.attestationca.portal.page.controllers;
import hirs.attestationca.persist.CriteriaModifier;
import hirs.attestationca.persist.FilteredRecordsList;
import hirs.attestationca.persist.entity.userdefined.Certificate;
import hirs.attestationca.persist.entity.userdefined.ReferenceManifest;
import hirs.attestationca.persist.service.ReferenceDigestValueService;
import hirs.attestationca.persist.service.ReferenceDigestValueServiceImpl;
import hirs.attestationca.persist.service.ReferenceManifestService;
import hirs.attestationca.persist.service.ReferenceManifestServiceImpl;
import hirs.attestationca.portal.datatables.DataTableInput;
import hirs.attestationca.portal.datatables.DataTableResponse;
import hirs.attestationca.portal.datatables.OrderedListQueryDataTableAdapter;
import hirs.attestationca.portal.page.Page;
import hirs.attestationca.portal.page.PageController;
import hirs.attestationca.portal.page.params.NoPageParams;
import jakarta.persistence.EntityManager;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.CriteriaQuery;
import jakarta.persistence.criteria.Root;
import jakarta.validation.Valid;
import lombok.extern.log4j.Log4j2;
import org.hibernate.Session;
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.lang.ref.Reference;
/**
* Controller for the Reference Manifest page.
*/
@Log4j2
@Controller
@RequestMapping("/reference-manifests")
public class ReferenceManifestPageController extends PageController<NoPageParams> {
@Autowired(required = false)
private EntityManager entityManager;
private final ReferenceManifestService referenceManifestManager;
private final ReferenceDigestValueService referenceEventManager;
/**
* 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 ReferenceManifestServiceImpl referenceManifestManager,
final ReferenceDigestValueServiceImpl referenceEventManager) {
super(Page.REFERENCE_MANIFESTS);
this.referenceManifestManager = referenceManifestManager;
this.referenceEventManager = referenceEventManager;
}
/**
* 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(
@Valid final DataTableInput input) {
log.info("Handling request for summary list: " + input);
// return this.referenceManifestManager.fetchReferenceManifests(input);
String orderColumnName = input.getOrderColumnName();
log.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 CriteriaQuery criteriaQuery) {
Session session = entityManager.unwrap(Session.class);
CriteriaBuilder cb = session.getCriteriaBuilder();
Root<ReferenceManifest> rimRoot = criteriaQuery.from(Reference.class);
criteriaQuery.select(rimRoot).distinct(true).where(cb.isNull(rimRoot.get(Certificate.ARCHIVE_FIELD)));
// criteria.add(Restrictions.isNull(Certificate.ARCHIVE_FIELD));
}
};
FilteredRecordsList<ReferenceManifest> records
= OrderedListQueryDataTableAdapter.getOrderedList(
ReferenceManifest.class,
referenceManifestManager,
input, orderColumnName, criteriaModifier);
log.debug("Returning list of size: " + records.size());
return new DataTableResponse<>(records, input);
}
}

View File

@ -0,0 +1,122 @@
package hirs.attestationca.portal.page.controllers;
import hirs.attestationca.persist.entity.userdefined.rim.ReferenceDigestValue;
import hirs.attestationca.persist.service.ReferenceDigestValueService;
import hirs.attestationca.persist.service.ReferenceDigestValueServiceImpl;
import hirs.attestationca.persist.service.ReferenceManifestService;
import hirs.attestationca.persist.service.ReferenceManifestServiceImpl;
import hirs.attestationca.portal.page.Page;
import hirs.attestationca.portal.page.PageController;
import hirs.attestationca.portal.page.params.NoPageParams;
import jakarta.validation.Valid;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.jpa.datatables.mapping.DataTablesInput;
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.List;
/**
* Controller for the TPM Events page.
*/
@Log4j2
@Controller
@RequestMapping("/rim-database")
public class RimDatabasePageController extends PageController<NoPageParams> {
private final ReferenceManifestService referenceManifestManager;
private final ReferenceDigestValueService referenceEventManager;
/**
* Constructor providing the Page's display and routing specification.
*
* @param referenceManifestManager the ReferenceManifestManager object
* @param referenceEventManager the referenceEventManager object
*/
@Autowired
public RimDatabasePageController(final ReferenceManifestServiceImpl referenceManifestManager,
final ReferenceDigestValueServiceImpl referenceEventManager) {
super(Page.RIM_DATABASE);
this.referenceManifestManager = referenceManifestManager;
this.referenceEventManager = referenceEventManager;
}
/**
* 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 List<ReferenceDigestValue> getTableData(
@Valid final DataTablesInput input) {
log.info("Handling request for summary list: " + input);
return this.referenceEventManager.fetchDigestValues();
// String orderColumnName = input.getOrderColumnName();
// log.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));
// }
// };
//
// log.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.updateRefDigestValue(rdv);
// } catch (DBManagerException e) {
// log.error("Failed to update TPM Event with Base RIM ID");
// log.error(rdv);
// }
// }
// }
// }
//
// return new DataTableResponse<>(referenceDigestValues, input);
}
}

View File

@ -0,0 +1,37 @@
# 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}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,964 @@
<%@ page contentType="text/html" pageEncoding="UTF-8"%>
<%-- JSP TAGS--%>
<%@taglib prefix="c" uri="jakarta.tags.core" %>
<%@taglib prefix="spring" uri="http://www.springframework.org/tags"%>
<%@taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<%@taglib prefix="my" tagdir="/WEB-INF/tags"%>
<%--CONTENT--%>
<my:page>
<jsp:attribute name="style">
<link type="text/css" rel="stylesheet" href="${common}/certificate_details.css"/>
</jsp:attribute>
<jsp:attribute name="pageHeaderTitle">
<c:choose>
<c:when test="${param.type=='certificateauthority'}">
Certificate Authority
<a href="${portal}/certificate-request/trust-chain/download?id=${param.id}">
<img src="${icons}/ic_file_download_black_24dp.png" title="Download Certificate">
</a>
</c:when>
<c:when test="${param.type=='endorsement'}">
Endorsement Certificate
<a href="${portal}/certificate-request/endorsement-key-credentials/download?id=${param.id}">
<img src="${icons}/ic_file_download_black_24dp.png" title="Download Certificate">
</a>
</c:when>
<c:when test="${param.type=='platform'}">
Platform Certificate
<a href="${portal}/certificate-request/platform-credentials/download?id=${param.id}">
<img src="${icons}/ic_file_download_black_24dp.png" title="Download Certificate">
</a>
</c:when>
<c:when test="${param.type=='issued'}">
Issued Attestation Certificates
<a href="${portal}/certificate-request/issued-certificates/download?id=${param.id}">
<img src="${icons}/ic_file_download_black_24dp.png" title="Download Certificate">
</a>
</c:when>
<c:otherwise>
Unknown Certificate
</c:otherwise>
</c:choose>
</jsp:attribute>
<jsp:body>
<div id="certificate-details-page" class="container-fluid">
<div class="row">
<div class="col-md-1 col-md-offset-1"><span class="colHeader">Issuer</span></div>
<div id="issuer" class="col col-md-8">
<!-- Display the issuer, and provide a link to the issuer details if provided -->
<div>Distinguished Name:&nbsp;<span>
<c:choose>
<c:when test="${not empty initialData.issuerID}">
<a href="${portal}/certificate-details?id=${initialData.issuerID}&type=certificateauthority">
${initialData.issuer}
</a>
</c:when>
<c:otherwise>
${initialData.issuer}
</c:otherwise>
</c:choose>
</span>
</div>
<div>Authority Key Identifier:&nbsp;
<span id="authorityKeyIdentifier"></span>
</div>
<c:if test="${not empty initialData.authInfoAccess}">
<div>Authority Info Access:&nbsp;<span>
<a href="${initialData.authInfoAccess}">${initialData.authInfoAccess}</a>
</span>
</div>
</c:if>
<c:if test="${not empty initialData.authSerialNumber}">
<div>Authority Serial Number:&nbsp;
<span id="authSerialNumber"></span>
</div>
</c:if>
<c:if test="${param.type!='issued'}">
<span class="chainIcon">
<!-- Icon with link for missing certificate for the chain -->
<c:choose>
<c:when test="${initialData.isSelfSigned == 'true'}">
<img src="${icons}/ic_all_inclusive_black_24dp.png"
title="Self sign certificate.">
</c:when>
<c:when test="${empty initialData.missingChainIssuer}">
<img src="${icons}/ic_checkbox_marked_circle_black_green_24dp.png"
title="All certificates in the chain were found.">
</c:when>
<c:otherwise>
<img src="${icons}/ic_error_red_24dp.png"
title="${initialData.missingChainIssuer}">
</c:otherwise>
</c:choose>
</span>
</c:if>
</div>
</div>
<c:if test="${not empty initialData.subject}">
<div class="row">
<div class="col-md-1 col-md-offset-1"><span class="colHeader">Subject</span></div>
<div id="subject" class="col col-md-8">${initialData.subject}</div>
</div>
</c:if>
<c:if test="${not empty initialData.serialNumber}">
<div class="row">
<div class="col-md-1 col-md-offset-1"><span class="colHeader">Serial Number</span></div>
<div id="serialNumber" class="col col-md-8 vertical"></div>
</div>
</c:if>
<c:if test="${not empty initialData.beginValidity}">
<div class="row">
<div class="col-md-1 col-md-offset-1"><span class="colHeader">Validity</span></div>
<div id="validity" class="col col-md-8">
<div>Not Before:&nbsp;<span>${initialData.beginValidity}</span></div>
<div>Not After:&nbsp;<span>${initialData.endValidity}</span></div>
</div>
</div>
</c:if>
<div class="row">
<div class="col-md-1 col-md-offset-1"><span class="colHeader">Signature</span></div><div id="signatureSection" class="col col-md-8">
<div class="panel-body">
<div class="panel panel-default">
<div class="panel-heading">
<a role="button" data-toggle="collapse" class="collapsed" href="#signatureComponentcollapse"
aria-expanded="true" data-placement="top" aria-controls="signatureComponentcollapse">
Signature
</a>
</div>
<div id="signatureComponentcollapse" class="panel-body collapse" role="tabpanel" aria-labelledby="headingOne" aria-expanded="false">
<div id="signature" class="fieldValue"></div>
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading">
<a role="button" data-toggle="collapse" class="collapsed" href="#signatureSizecollapse"
aria-expanded="true" data-placement="top" aria-controls="signatureSizecollapse">
Algorithm
</a>
</div>
<div id="signatureSizecollapse" class="panel-body collapse" role="tabpanel" aria-labelledby="headingOne" aria-expanded="false">
<div>
<span class="fieldValue">
${initialData.signatureAlgorithm} / ${initialData.signatureSize}
</span>
</div>
</div>
</div>
</div>
</div>
</div>
<c:if test="${not empty initialData.encodedPublicKey}">
<div class="row">
<div class="col-md-1 col-md-offset-1"><span class="colHeader">Public Key</span></div>
<div id="publicKeySection" class="col col-md-8">
<div class="panel-body">
<div class="panel panel-default">
<div class="panel-heading">
<a role="button" data-toggle="collapse" class="collapsed" href="#publicKeycollapse"
aria-expanded="true" data-placement="top" aria-controls="publicKeycollapse">
Public Key
</a>
</div>
<div id="publicKeycollapse" class="panel-body collapse" role="tabpanel" aria-labelledby="headingOne" aria-expanded="false">
<c:choose>
<c:when test="${not empty initialData.publicKeyValue}">
<div id="encodedPublicKey" class="fieldValue"></div>
</c:when>
<c:otherwise>
<div id="encodedPublicKey" class="fieldValue"></div>
</c:otherwise>
</c:choose>
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading">
<a role="button" data-toggle="collapse" class="collapsed" href="#publicKeySizecollapse"
aria-expanded="true" data-placement="top" aria-controls="publicKeySizecollapse">
Algorithm
</a>
</div>
<div id="publicKeySizecollapse" class="panel-body collapse" role="tabpanel" aria-expanded="false">
<div>
<span class="fieldValue">
${initialData.publicKeyAlgorithm} / ${initialData.publicKeySize}
</span>
</div>
</div>
</div>
</div>
</div>
</div>
</c:if>
<div class="row">
<div class="col-md-1 col-md-offset-1"><span class="colHeader">X509 Credential Version</span></div>
<div id="credentialVersion" class="col col-md-8 vertical">${initialData.x509Version} (v${initialData.x509Version + 1})</div>
</div>
<div class="row">
<div class="col-md-1 col-md-offset-1"><span class="colHeader">Credential Type</span></div>
<div id="credentialType" class="col col-md-8 vertical">${initialData.credentialType}</div>
</div>
<!-- Add the different fields based on the certificate type -->
<c:choose>
<c:when test="${param.type=='certificateauthority'}">
<c:choose>
<c:when test="${not empty initialData.subjectKeyIdentifier}">
<div class="row">
<div class="col-md-1 col-md-offset-1"><span class="colHeader">Subject Key Identifier</span></div>
<div id="subjectKeyIdentifier" class="col col-md-8 vertical"></div>
</div>
</c:when>
</c:choose>
<c:if test="${initialData.crlPoints}">
<div class="row">
<div class="col-md-1 col-md-offset-1"><span class="colHeader">Revocation Locator</span></div>
<div id="revocationLocator" class="col col-md-8"><a href="${initialData.crlPoints}">${initialData.crlPoints}</div>
</div>
</c:if>
<div class="row">
<div class="col-md-1 col-md-offset-1"><span class="colHeader">Key Usage</span></div>
<c:choose>
<c:when test="${not empty initialData.keyUsage}">
<div id="keyUsage" class="col col-md-8 vertical">${initialData.keyUsage}</div>
</c:when>
<c:otherwise>
<div id="keyUsage" class="col col-md-8 vertical">Not Specified</div>
</c:otherwise>
</c:choose>
</div>
<c:choose>
<c:when test="${not empty initialData.extendedKeyUsage}">
<div class="row">
<div class="col-md-1 col-md-offset-1"><span class="colHeader">Extended Key Usage</span></div>
<div id="extendedKeyUsage" class="col col-md-8 vertical">${initialData.extendedKeyUsage}</div>
</div>
</c:when>
</c:choose>
</c:when>
<c:when test="${param.type=='endorsement'}">
<div class="row">
<div class="col-md-1 col-md-offset-1"><span class="colHeader">System Information</span></div>
<div id="subjectAltName" class="col col-md-8">
<div id="manufacturer">Manufacturer:&nbsp;<span>${initialData.manufacturer}</span></div>
<div id="model">Model:&nbsp;<span>${initialData.model}</span></div>
<div id="version">Version:&nbsp;<span>${initialData.version}</span></div>
</div>
</div>
<div class="row">
<div class="col-md-1 col-md-offset-1"><span class="colHeader">Policy Reference</span></div>
<div id="policyReference" class="col col-md-8 vertical">
<c:choose>
<c:when test="${not empty initialData.policyReference}">
${initialData.policyReference}
</c:when>
<c:otherwise>
Not Specified
</c:otherwise>
</c:choose>
</div>
</div>
<c:if test="${initialData.crlPoints}">
<div class="row">
<div class="col-md-1 col-md-offset-1"><span class="colHeader">Revocation Locator</span></div>
<div id="revocationLocator" class="col col-md-8"><a href="${initialData.crlPoints}">${initialData.crlPoints}</div>
</div>
</c:if>
<div class="row">
<div class="col-md-1 col-md-offset-1"><span class="colHeader">Key Usage</span></div>
<c:choose>
<c:when test="${not empty initialData.keyUsage}">
<div id="keyUsage" class="col col-md-8 vertical">${initialData.keyUsage}</div>
</c:when>
<c:otherwise>
<div id="keyUsage" class="col col-md-8 vertical">Not Specified</div>
</c:otherwise>
</c:choose>
</div>
<c:choose>
<c:when test="${not empty initialData.extendedKeyUsage}">
<div class="row">
<div class="col-md-1 col-md-offset-1"><span class="colHeader">Extended Key Usage</span></div>
<div id="extendedKeyUsage" class="col col-md-8 vertical">${initialData.extendedKeyUsage}</div>
</div>
</c:when>
</c:choose>
<div class="row">
<div class="col-md-1 col-md-offset-1">
<span class="colHeader">
<a role="button" data-toggle="collapse" class="collapsed" href="#tpmSpecificationInner"
aria-expanded="true" data-placement="top" aria-controls="tpmSpecificationInner">
TPM Specification
</a>
</span>
</div>
<div id="tpmSpecification" class="col col-md-8">
<div id="tpmSpecificationInner" class="panel-body collapse" role="tabpanel" aria-expanded="false">
<div>Family:&nbsp;<span>${initialData.TPMSpecificationFamily}</span></div>
<div>Level:&nbsp;<span>${initialData.TPMSpecificationLevel}</span></div>
<div>Revision:&nbsp;<span>${initialData.TPMSpecificationRevision}</span></div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-1 col-md-offset-1">
<span class="colHeader">
<a role="button" data-toggle="collapse" class="collapsed" href="#tpmSecurityAssertionInner"
aria-expanded="true" data-placement="top" aria-controls="tpmSecurityAssertionInner">
TPM Security Assertion
</a>
</span>
</div>
<div id="tpmSecurityAssertion" class="col col-md-8">
<div id="tpmSecurityAssertionInner" class="panel-body collapse" role="tabpanel" aria-expanded="false">
<div>Version:&nbsp;<span>${initialData.TPMSecurityAssertionsVersion}</span></div>
<div>Field Upgradeable:&nbsp;<span>${initialData.TPMSecurityAssertionsFieldUpgradeable}</span></div>
<div>ek Generation Type:&nbsp;<span>${initialData.TPMSecurityAssertionsEkGenType}</span></div>
<div>ek Generation Location:&nbsp;<span>${initialData.TPMSecurityAssertionsEkGenLoc}</span></div>
<div>ek Certificate Generation Location:&nbsp;<span>${initialData.TPMSecurityAssertionsEkCertGenLoc}</span></div>
</div>
</div>
</div>
</c:when>
<c:when test="${param.type=='platform'}">
<c:if test="${not empty initialData.platformType}">
<div class="row">
<div class="col-md-1 col-md-offset-1"><span class="colHeader">Platform Type</span></div>
<div id="platformType" class="col col-md-8 vertical">${initialData.platformType}</div>
</div>
<div class="row">
<div class="col-md-1 col-md-offset-1"><span class="colHeader">Platform Chain</span></div>
<div id="platformType" class="col col-md-8 vertical">
<span>
<c:forEach items="${initialData.chainCertificates}" var="credential" varStatus="loop">
<c:choose>
<c:when test="${initialData.certificateId==credential.getId().toString()}">
${loop.index}&nbsp;
</c:when>
<c:otherwise>
<a href="${portal}/certificate-details?id=${credential.getId()}&type=platform">${loop.index}</a>&nbsp;
</c:otherwise>
</c:choose>
</c:forEach>
</span>
</div>
</div>
</c:if>
<c:if test="${not empty initialData.CPSuri}">
<div class="row">
<div class="col-md-1 col-md-offset-1"><span class="colHeader">Certification Practice Statement URI</span></div>
<div id="certificateCPSU" class="col col-md-8 vertical">
<a href="${initialData.CPSuri}"> ${initialData.CPSuri}</a>
</div>
</div>
</c:if>
<div class="row">
<div class="col-md-1 col-md-offset-1"><span class="colHeader">Holder</span></div>
<div id="holder" class="col col-md-8">
<c:if test="${not empty initialData.holderIssuer}">
<div>Holder Certificate:&nbsp;<span>${initialData.holderIssuer}</span></div>
</c:if>
<div id="certificateid">
<div>Holder Identifier:&nbsp;
<c:choose>
<c:when test="${not empty initialData.holderId}">
<span>
<c:choose>
<c:when test="${(not empty initialData.platformType) and (initialData.platformType=='Delta')}">
<a href="${portal}/certificate-details?id=${initialData.holderId}&type=platform">
${initialData.holderSerialNumber}
</a>
</c:when>
<c:otherwise>
<a href="${portal}/certificate-details?id=${initialData.holderId}&type=endorsement">
${initialData.holderSerialNumber}
</a>
</c:otherwise>
</c:choose>
</span>
</c:when>
<c:otherwise>
<span>${initialData.holderSerialNumber}</span>
</c:otherwise>
</c:choose>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-1 col-md-offset-1"><span class="colHeader">System Platform Information</span></div>
<div id="subjectAltName" class="col col-md-8">
<div id="manufacturer">Manufacturer:&nbsp;<span>${initialData.manufacturer}</span></div>
<div id="model">Model:&nbsp;<span>${initialData.model}</span></div>
<div id="version">Version:&nbsp;<span>${initialData.version}</span></div>
<div id="serial">Serial Number:&nbsp;<span>${initialData.platformSerial}</span></div>
</div>
</div>
<div class="row">
<div class="col-md-1 col-md-offset-1"><span class="colHeader">TCG Platform Specification Version</span></div>
<div id="majorVersion" class="col col-md-8 vertical">${initialData.majorVersion}.${initialData.minorVersion}.${initialData.revisionLevel}</div>
</div>
<div class="row">
<div class="col-md-1 col-md-offset-1"><span class="colHeader">Platform Class</span></div>
<div id="platformClass" class="col col-md-8 vertical">${initialData.platformClass}</div>
</div>
<!-- TBB Security Assertion-->
<c:if test="${not empty initialData.tbbSecurityAssertion}">
<div class="row">
<div class="col-md-1 col-md-offset-1"><span class="colHeader">TBB Security Assertion</span></div>
<div id="tbbsecurity" class="col col-md-8">
<div class="tbbsecurityLine">
<span class="fieldHeader">Version:</span>
<span class="fieldValue">${initialData.tbbSecurityAssertion.getVersion()}&nbsp;(v${initialData.tbbSecurityAssertion.getVersion().getValue() + 1})</span>
</div>
<div class="tbbsecurityLine">
<span class="fieldHeader">RTM (Root of Trust of Measurement):</span>
<span class="fieldValue">${fn:toUpperCase(initialData.tbbSecurityAssertion.getRtmType().getValue())}</span>
</div>
<!-- CCINFO -->
<c:if test="${not empty initialData.tbbSecurityAssertion.getCcInfo()}">
<c:set var="ccinfo" value="${initialData.tbbSecurityAssertion.getCcInfo()}" />
<div class="panel panel-default">
<div class="panel-heading" role="tab" id="headingOne">
<h4 class="panel-title">
<a role="button" data-toggle="collapse" data-parent="#tbbsecurity" class="collapsed"
href="#ccinfocollapse" aria-expanded="false" aria-controls="ccinfocollapse">
Common Criteria Measures Information
</a>
</h4>
</div>
<div id="ccinfocollapse" class="panel-collapse collapse" role="tabpanel" aria-labelledby="headingOne">
<div class="panel-body">
<div id="ccinfo" class="row">
<div class="tbbsecurityLine">
<span class="fieldHeader">Version:</span>
<span class="fieldValue">${ccinfo.getVersion()}</span>
</div>
<div class="tbbsecurityLine">
<span class="fieldHeader">Assurance Level:</span>
<span class="fieldValue">${fn:toUpperCase(ccinfo.getAssurancelevel().getValue())}</span>
</div>
<div class="tbbsecurityLine">
<span class="fieldHeader">Evaluation Status:</span>
<span class="fieldValue">${fn:toUpperCase(ccinfo.getEvaluationStatus().getValue())}</span>
</div>
<div class="tbbsecurityLine">
<c:choose>
<c:when test="${ccinfo.getPlus()=='TRUE'}">
<span class="label label-success">Plus</span>
</c:when>
<c:otherwise>
<span class="label label-danger">Not Plus</span>
</c:otherwise>
</c:choose>
</div>
<div class="tbbsecurityLine">
<span class="fieldHeader">Strength of Function:</span>
<span class="fieldValue">${fn:toUpperCase(ccinfo.getStrengthOfFunction().getValue())}</span>
</div>
<c:if test="${not empty ccinfo.getProfileOid()}">
<div class="tbbsecurityLine">
<span class="fieldHeader">Profile OID:</span>
<span class="fieldValue">${ccinfo.getProfileOid()}</span>
</div>
</c:if>
<c:if test="${not empty ccinfo.getProfileUri()}">
<div class="tbbsecurityLine">
<span class="fieldHeader">Profile URI:</span>
<span class="fieldValue">
<a href="${ccinfo.getProfileUri().getUniformResourceIdentifier()}">
${ccinfo.getProfileUri().getUniformResourceIdentifier()}
</a>
</span>
</div>
<c:if test="${not empty ccinfo.getProfileUri().getHashAlgorithm()}">
<div class="tbbsecurityLine">
<span class="fieldHeader">Profile Hash Algorithm:</span>
<span class="fieldValue">${ccinfo.getProfileUri().getHashAlgorithm()}</span>
</div>
</c:if>
<c:if test="${not empty ccinfo.getProfileUri().getHashValue()}">
<div class="tbbsecurityLine">
<span class="fieldHeader">Profile Hash Value:</span>
<span class="fieldValue">${ccinfo.getProfileUri().getHashValue()}</span>
</div>
</c:if>
</c:if>
<c:if test="${not empty ccinfo.getTargetOid()}">
<div class="tbbsecurityLine">
<span class="fieldHeader">Target OID:</span>
<span class="fieldValue">${ccinfo.getTargetOid()}</span>
</div>
</c:if>
<c:if test="${not empty ccinfo.getTargetUri()}">
<div class="tbbsecurityLine">
<span class="fieldHeader">Target URI:</span>
<span class="fieldValue">
<a href="${ccinfo.getTargetUri().getUniformResourceIdentifier()}">
${ccinfo.getTargetUri().getUniformResourceIdentifier()}
</a>
</span>
</div>
<c:if test="${not empty ccinfo.getTargetUri().getHashAlgorithm()}">
<div class="tbbsecurityLine">
<span class="fieldHeader">Target Hash Algorithm:</span>
<span class="fieldValue">${ccinfo.getTargetUri().getHashAlgorithm()}</span>
</div>
</c:if>
<c:if test="${not empty ccinfo.getTargetUri().getHashValue()}">
<div class="tbbsecurityLine">
<span class="fieldHeader">Target Hash Value:</span>
<span class="fieldValue">${ccinfo.getTargetUri().getHashValue()}</span>
</div>
</c:if>
</c:if>
</div>
</div>
</div>
</div>
</c:if>
<!-- FIPS Level -->
<c:if test="${not empty initialData.tbbSecurityAssertion.getFipsLevel()}">
<c:set var="fipslevel" value="${initialData.tbbSecurityAssertion.getFipsLevel()}" />
<div class="panel panel-default">
<div class="panel-heading" role="tab" id="headingThree">
<h4 class="panel-title">
<a role="button" data-toggle="collapse" data-parent="#tbbsecurity" class="collapsed"
href="#fipscollapse" aria-expanded="false" aria-controls="fipscollapse">
FIPS Level
</a>
</h4>
</div>
<div id="fipscollapse" class="panel-collapse collapse" role="tabpanel" aria-labelledby="headingTwo">
<div class="panel-body">
<div id="fipsLevel" class="row">
<div class="tbbsecurityLine">
<span class="fieldHeader">Version:</span>
<span class="fieldValue">${fipslevel.getVersion()}</span>
</div>
<div class="tbbsecurityLine">
<span class="fieldHeader">Level:</span>
<span class="fieldValue">${fn:toUpperCase(fipslevel.getLevel().getValue())}</span>
</div>
<div class="tbbsecurityLine">
<c:choose>
<c:when test="${fipslevel.getPlus()=='TRUE'}">
<span class="label label-success">Plus</span>
</c:when>
<c:otherwise>
<span class="label label-danger">Not Plus</span>
</c:otherwise>
</c:choose>
</div>
</div>
</div>
</div>
</div>
</c:if>
<!-- ISO9000 isCertified and URI -->
<div class="panel panel-default">
<div class="panel-heading" role="tab" id="headingThree">
<h4 class="panel-title">
<a role="button" data-toggle="collapse" data-parent="#tbbsecurity" class="collapsed"
href="#iso9000collapse" aria-expanded="false" aria-controls="iso9000collapse">
ISO 9000
</a>
</h4>
</div>
<div id="iso9000collapse" class="panel-collapse collapse" role="tabpanel" aria-labelledby="headingThree">
<div class="panel-body">
<div id="iso9000" class="row">
<div class="tbbsecurityLine">
<c:choose>
<c:when test="${initialData.tbbSecurityAssertion.getIso9000Certified()=='TRUE'}">
<span class="label label-success">ISO 9000 Certified</span>
</c:when>
<c:otherwise>
<span class="label label-danger">ISO 9000 Not Certified</span>
</c:otherwise>
</c:choose>
</div>
<c:if test="${not empty initialData.tbbSecurityAssertion.getIso9000Uri()}">
<div class="tbbsecurityLine">
<span class="fieldHeader">URI:</span>
<span class="fieldValue">
<a href="${initialData.tbbSecurityAssertion.getIso9000Uri()}">
${initialData.tbbSecurityAssertion.getIso9000Uri()}
</a>
</span>
</div>
</c:if>
</div>
</div>
</div>
</div>
</div>
</div>
</c:if>
<!-- For PC 2.0 -->
<c:if test="${fn:contains(initialData.credentialType, 'TCG')}">
<div class="row">
<div class="col-md-1 col-md-offset-1"><span class="colHeader">TCG Platform Configuration</span></div>
<div id="platformConfiguration" class="col col-md-8">
<c:if test="${not empty initialData.componentsIdentifier}">
<!-- Component Identifier -->
<div class="panel panel-default">
<div class="panel-heading" role="tab" id="headingOne">
<h4 class="panel-title">
<a role="button" data-toggle="collapse" data-parent="#platformConfiguration" class="collapsed"
href="#componentIdentifiercollapse" aria-expanded="true" aria-controls="componentIdentifiercollapse">
Components
</a>
</h4>
</div>
<div id="componentIdentifiercollapse" class="panel-collapse collapse in" role="tabpanel" aria-labelledby="headingOne" aria-expanded="true">
<div class="panel-body">
<div id="componentIdentifier" class="row">
<c:forEach items="${initialData.componentsIdentifier}" var="component">
<c:set var="combined" value="${component.hashCode()}" scope="page"/>
<div class="component col col-md-4">
<div class="panel panel-default">
<c:choose>
<c:when test="${fn:contains(initialData.failures, combined)}">
<div class="panel-heading" style="background-color: red; color: white">
</c:when>
<c:otherwise>
<div class="panel-heading">
</c:otherwise>
</c:choose>
<c:choose>
<c:when test="${component.isVersion2()=='TRUE'}">
<span data-toggle="tooltip" data-placement="top" title="Component Class">${component.getComponentClass()}</span>
</c:when>
<c:otherwise>
<span data-toggle="tooltip" data-placement="top" title="Component Class">Platform Components</span>
</c:otherwise>
</c:choose>
</div>
<div class="panel-body">
<span class="fieldHeader">Manufacturer:</span>
<span class="fieldValue">${component.getComponentManufacturer()}</span><br/>
<span class="fieldHeader">Model:</span>
<span class="fieldValue">${component.getComponentModel()}</span><br/>
<c:if test="${not empty fn:trim(component.getComponentSerial())}">
<span class="fieldHeader">Serial Number:</span>
<span class="fieldValue">${component.getComponentSerial()}</span><br/>
</c:if>
<c:if test="${not empty fn:trim(component.getComponentRevision())}">
<span class="fieldHeader">Revision:</span>
<span class="fieldValue">${component.getComponentRevision()}</span><br/>
</c:if>
<c:forEach items="${component.getComponentAddress()}" var="address">
<span class="fieldHeader">${address.getAddressTypeValue()} address:</span>
<span class="fieldValue">${address.getAddressValue()}</span><br/>
</c:forEach>
<c:choose>
<c:when test="${component.getFieldReplaceable()=='TRUE'}">
<span class="label label-success">Replaceable</span><br/>
</c:when>
<c:otherwise>
<span class="label label-danger">Irreplaceable</span><br/>
</c:otherwise>
</c:choose>
<c:if test="${component.isVersion2()}">
<c:if test="${not empty component.getCertificateIdentifier()}">
<span class="fieldHeader">Platform Certificate Issuer:</span>
<span class="fieldValue">${component.getCertificateIdentifier().getIssuerDN()}</span><br />
<span class="fieldHeader">Platform Certificate Serial Number:</span>
<span class="fieldValue">${component.getCertificateIdentifier().getCertificateSerialNumber()}</span><br />
<span class="fieldHeader">Platform Certificate URI:</span>
</c:if>
<span class="fieldValue">
<a href="${component.getComponentPlatformUri().getUniformResourceIdentifier()}">
${component.getComponentPlatformUri().getUniformResourceIdentifier()}
</a>
</span><br />
<span class="fieldHeader">Status:</span>
<span class="fieldValue">${component.getAttributeStatus()}</span><br/>
</c:if>
</div>
</div>
</div>
</c:forEach>
</div>
</div>
</div>
</div>
</c:if>
<c:if test="${not empty initialData.componentsIdentifierURI}">
<!-- Components Identifier URI -->
<div class="panel panel-default">
<div class="panel-heading" role="tab" id="headingTwo">
<h4 class="panel-title">
<a role="button" data-toggle="collapse" data-parent="#platformConfiguration" class="collapsed"
href="#componentIdentifierURIcollapse" aria-expanded="false" aria-controls="componentIdentifierURIcollapse">
Components Identifier URI
</a>
</h4>
</div>
<div id="componentIdentifierURIcollapse" class="panel-collapse collapse" role="tabpanel" aria-labelledby="headingTwo">
<div class="panel-body">
<div id="componentIdentifierURI" class="row">
<span class="fieldHeader">URI:</span>
<a href="${initialData.componentsIdentifierURI.getUniformResourceIdentifier()}">
${initialData.componentsIdentifierURI.getUniformResourceIdentifier()}
</a>
<c:if test="${not empty initialData.componentsIdentifierURI.getHashAlgorithm()}">
<span class="fieldHeader">Hash Algorithm:</span>
<span>${initialData.componentsIdentifierURI.getHashAlgorithm()}</span>
</c:if>
<c:if test="${not empty initialData.componentsIdentifierURI.getHashValue()}">
<span class="fieldHeader">Hash Value:</span>
<span>${initialData.componentsIdentifierURI.getHashValue()}</span>
</c:if>
</div>
</div>
</div>
</div>
</c:if>
<c:if test="${not empty initialData.platformProperties}">
<!-- Platform Properties -->
<div class="panel panel-default">
<div class="panel-heading" role="tab" id="headingThree">
<h4 class="panel-title">
<a role="button" data-toggle="collapse" data-parent="#platformConfiguration" class="collapsed"
href="#platformPropertiescollapse" aria-expanded="false" aria-controls="platformPropertiescollapse">
Platform Properties
</a>
</h4>
</div>
<div id="platformPropertiescollapse" class="panel-collapse collapse" role="tabpanel" aria-labelledby="headingThree">
<div class="panel-body">
<div id="platformProperties" class="row">
<c:forEach items="${initialData.platformProperties}" var="property">
<div class="component col col-md-4">
<div class="panel panel-default">
<div class="panel-body">
<span class="fieldHeader">Name:</span>
<span class="fieldValue">${property.getPropertyName()}</span><br/>
<span class="fieldHeader">Value:</span>
<span class="fieldValue" style="word-wrap: break-word">${property.getPropertyValue()}</span><br/>
</div>
</div>
</div>
</c:forEach>
</div>
</div>
</div>
</div>
</c:if>
<c:if test="${not empty initialData.platformPropertiesURI}">
<!-- Platform Properties URI -->
<div class="panel panel-default">
<div class="panel-heading" role="tab" id="headingFour">
<h4 class="panel-title">
<a role="button" data-toggle="collapse" data-parent="#platformConfiguration" class="collapsed"
href="#platformPropertiesURIcollapse" aria-expanded="false" aria-controls="platformPropertiesURIcollapse">
Platform Properties URI
</a>
</h4>
</div>
<div id="platformPropertiesURIcollapse" class="panel-collapse collapse" role="tabpanel" aria-labelledby="headingFour">
<div class="panel-body">
<div id="platformPropertiesURI" class="row">
<span class="fieldHeader">URI:</span>
<a href="${initialData.platformPropertiesURI.getUniformResourceIdentifier()}">
${initialData.platformPropertiesURI.getUniformResourceIdentifier()}
</a>
<c:if test="${not empty initialData.platformPropertiesURI.getHashAlgorithm()}">
<span class="fieldHeader">Hash Algorithm:</span>
<span>${initialData.platformPropertiesURI.getHashAlgorithm()}</span>
</c:if>
<c:if test="${not empty initialData.platformPropertiesURI.getHashValue()}">
<span class="fieldHeader">Hash Value:</span>
<span>${initialData.platformPropertiesURI.getHashValue()}</span>
</c:if>
</div>
</div>
</div>
</div>
</c:if>
</div><!-- close platformConfiguration -->
</div> <!-- Close row -->
</c:if>
</c:when>
<c:when test="${param.type=='issued'}">
<div class="row">
<div class="col-md-1 col-md-offset-1"><span class="colHeader">System Information</span></div>
<div id="subjectAltName" class="col col-md-8">
<div id="manufacturer">Manufacturer:&nbsp;<span>${initialData.manufacturer}</span></div>
<div id="model">Model:&nbsp;<span>${initialData.model}</span></div>
<div id="version">Version:&nbsp;<span>${initialData.version}</span></div>
<div id="serial">Serial Number:&nbsp;<span>${initialData.platformSerial}</span></div>
</div>
</div>
<div class="row">
<div class="col-md-1 col-md-offset-1"><span class="colHeader">Policy Reference</span></div>
<div id="policyReference" class="col col-md-8 vertical">
<c:choose>
<c:when test="${not empty initialData.policyReference}">
${initialData.policyReference}
</c:when>
<c:otherwise>
Not Specified
</c:otherwise>
</c:choose>
</div>
</div>
<c:if test="${initialData.crlPoints}">
<div class="row">
<div class="col-md-1 col-md-offset-1"><span class="colHeader">Revocation Locator</span></div>
<div id="revocationLocator" class="col col-md-8"><a href="${initialData.crlPoints}">${initialData.crlPoints}</div>
</div>
</c:if>
<div class="row">
<div class="col-md-1 col-md-offset-1"><span class="colHeader">Endorsement Credential</span></div>
<div id="endorsementID" class="col col-md-8">
<c:if test="${not empty initialData.endorsementID}">
<a href="${portal}/certificate-details?id=${initialData.endorsementID}&type=endorsement">
<img src="${icons}/ic_vpn_key_black_24dp.png">
</a>
</c:if>
</div>
</div>
<div class="row">
<div class="col-md-1 col-md-offset-1"><span class="colHeader">Platform Credentials</span></div>
<div id="platformID" class="col col-md-8">
<c:if test="${not empty initialData.platformID}">
<c:forTokens items = "${initialData.platformID}" delims = "," var = "pcID">
<a href="${portal}/certificate-details?id=${pcID}&type=platform">
<img src="${icons}/ic_important_devices_black_24dp.png">
</a>
</c:forTokens>
</c:if>
</div>
</div>
<div class="row">
<div class="col-md-1 col-md-offset-1"><span class="colHeader">TCG Platform Specification Version</span></div>
<div id="majorVersion" class="col col-md-8 vertical">${initialData.majorVersion}.${initialData.minorVersion}.${initialData.revisionLevel}</div>
</div>
<div class="row">
<div class="col-md-1 col-md-offset-1"><span class="colHeader">TCG Credential Specification Version</span></div>
<div id="majorVersion" class="col col-md-8 vertical">${initialData.tcgMajorVersion}.${initialData.tcgMinorVersion}.${initialData.tcgRevisionLevel}</div>
</div>
<div class="row">
<div class="col-md-1 col-md-offset-1">
<span class="colHeader">
<a role="button" data-toggle="collapse" class="collapsed" href="#tpmSpecificationInner"
aria-expanded="true" data-placement="top" aria-controls="tpmSpecificationInner">
TPM Specification
</a>
</span>
</div>
<div id="tpmSpecification" class="col col-md-8">
<div id="tpmSpecificationInner" class="panel-body collapse" role="tabpanel" aria-expanded="false">
<div>Family:&nbsp;<span>${initialData.TPMSpecificationFamily}</span></div>
<div>Level:&nbsp;<span>${initialData.TPMSpecificationLevel}</span></div>
<div>Revision:&nbsp;<span>${initialData.TPMSpecificationRevision}</span></div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-1 col-md-offset-1">
<span class="colHeader">
<a role="button" data-toggle="collapse" class="collapsed" href="#tpmSecurityAssertionInner"
aria-expanded="true" data-placement="top" aria-controls="tpmSecurityAssertionInner">
TPM Security Assertion
</a>
</span>
</div>
<div id="tpmSecurityAssertion" class="col col-md-8">
<div id="tpmSecurityAssertionInner" class="panel-body collapse" role="tabpanel" aria-expanded="false">
<div>Version:&nbsp;<span>${initialData.TPMSecurityAssertionsVersion}</span></div>
<div>Field Upgradeable:&nbsp;<span>${initialData.TPMSecurityAssertionsFieldUpgradeable}</span></div>
<div>ek Generation Type:&nbsp;<span>${initialData.TPMSecurityAssertionsEkGenType}</span></div>
<div>ek Generation Location:&nbsp;<span>${initialData.TPMSecurityAssertionsEkGenLoc}</span></div>
<div>ek Certificate Generation Location:&nbsp;<span>${initialData.TPMSecurityAssertionsEkCertGenLoc}</span></div>
</div>
</div>
</div>
</c:when>
</c:choose>
</div>
<script>
$(document).ready(function () {
var type = "${param.type}";
var signature = ${initialData.signature};
var serialNumber = '${initialData.serialNumber}';
var authorityKeyIdentifier = '${initialData.authKeyId}';
var authoritySerialNumber = '${initialData.authSerialNumber}';
//Format validity time
$("#validity span").each(function () {
$(this).text(formatDateTime($(this).text()));
});
//Convert byte array to string
$("#signature").html(byteToHexString(signature));
//Convert byte array to string
$("#serialNumber").html(parseSerialNumber(serialNumber));
// authority key ID
<c:choose>
<c:when test="${not empty initialData.authKeyId}">
$("#authorityKeyIdentifier").html(parseSerialNumber(authorityKeyIdentifier));
</c:when>
<c:otherwise>
$("#authorityKeyIdentifier").html("Not Specified");
</c:otherwise>
</c:choose>
<c:if test="${not empty initialData.authSerialNumber}">
//Convert string to serial String
$("#authSerialNumber").html(parseSerialNumber(authoritySerialNumber));
</c:if>
<c:choose>
<c:when test="${not empty initialData.publicKeyValue}">
var publicKey = '${initialData.publicKeyValue}';
$("#encodedPublicKey").html(parseHexString(publicKey));
</c:when>
<c:otherwise>
<c:if test="${not empty initialData.encodedPublicKey}">
//Change public key byte to hex
var encPublicKey = ${initialData.encodedPublicKey};
$("#encodedPublicKey").html(byteToHexString(encPublicKey));
</c:if>
</c:otherwise>
</c:choose>
<c:if test="${not empty initialData.subjectKeyIdentifier}">
//Change subject byte to hex only for CACertificate
if (type === "certificateauthority") {
var subjectKeyIdentifier = ${initialData.subjectKeyIdentifier};
$("#subjectKeyIdentifier").html(byteToHexString(subjectKeyIdentifier));
}
</c:if>
//Initilize tooltips
$('[data-toggle="tooltip"]').tooltip();
//Vertical alignment on data columns
$('.vertical').each(function () {
$(this).css({
'line-height': $(this).height() + 'px'
});
});
//Change link width
$("#headingOne, #headingTwo, #headingThree").each(function (e) {
var width = $(this).width();
//Get link width
var linkWidth = $(this).find('a').width();
//Change width for the link
$(this).find('a').css({
"padding-right": (width - linkWidth) + "px"
});
});
});
</script>
</jsp:body>
</my:page>

View File

@ -0,0 +1,131 @@
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<%-- JSP TAGS --%>
<%@taglib prefix="c" uri="jakarta.tags.core" %>
<%@taglib prefix="spring" uri="http://www.springframework.org/tags"%>
<%@taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<%@taglib prefix="my" tagdir="/WEB-INF/tags"%>
<%-- CONTENT --%>
<my:page>
<jsp:attribute name="script">
<script type="text/javascript" src="${lib}/jquery.spring-friendly/jquery.spring-friendly.js"></script>
</jsp:attribute>
<jsp:attribute name="pageHeaderTitle">Issued Certificates</jsp:attribute>
<jsp:body>
<div class="aca-input-box-header">
Issued Credentials
<a href="${portal}/certificate-request/issued-certificates/bulk">
<img src="${icons}/ic_file_download_black_24dp.png" title="Download All Issued Certificates">
</a>
</div>
<br />
<div class="aca-data-table">
<table id="issuedTable" class="display" width="100%">
<thead>
<tr>
<th rowspan="2">Hostname</th>
<th rowspan="2">Issuer</th>
<th rowspan="2">Valid (begin)</th>
<th rowspan="2">Valid (end)</th>
<th colspan="2">Credentials</th>
<th rowspan="2">Options</th>
</tr>
<tr>
<th>Endorsement</th>
<th>Platform</th>
</tr>
</thead>
</table>
</div>
<script>
$(document).ready(function() {
var url = pagePath + '/list';
var columns = [
{
data: 'device.name',
render: function (data, type, full, meta) {
// if there's a device, display its name, otherwise
// display nothing
if (full.device) {
// TODO render a link to a device details page,
// passing the device.id
return full.device.name;
}
return '';
}
},
{data: 'issuer'},
{
data: 'beginValidity',
searchable:false,
render: function (data, type, full, meta) {
return formatDateTime(data);
}
},
{
data: 'endValidity',
searchable:false,
render: function (data, type, full, meta) {
return formatDateTime(data);
}
},
{
data: 'id',
orderable: false,
searchable:false,
render: function (data, type, full, meta) {
//Display endorsement credential
var html = '';
if (full.endorsementCredential !== undefined
&& full.endorsementCredential !== null){
var id = full.endorsementCredential.id;
html += certificateDetailsLink('endorsement', id, false) +'&nbsp;';
}
return html;
}
},
{
data: 'id',
orderable: false,
searchable:false,
render: function (data, type, full, meta) {
//Display platform credential
var html = '';
if (full.platformCredentials !== undefined
&& full.platformCredentials !== null) {
var size = full.platformCredentials.length;
for(var i = 0; i < size; i++) {
var id = full.platformCredentials[i].id;
html += certificateDetailsLink('platform', id, false) +'&nbsp;';
}
}
return html;
}
},
{
data: 'id',
orderable: false,
searchable:false,
render: function(data, type, full, meta) {
// set up link to details page
var html = '';
html += certificateDetailsLink('issued', full.id, true);
html += certificateDownloadLink(full.id, pagePath);
html += certificateDeleteLink(full.id, pagePath);
return html;
}
}
];
//Set data tables
setDataTables("#issuedTable", url, columns);
});
</script>
</jsp:body>
</my:page>

View File

@ -0,0 +1,136 @@
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<%-- JSP TAGS --%>
<%@taglib prefix="c" uri="jakarta.tags.core" %>
<%@taglib prefix="spring" uri="http://www.springframework.org/tags"%>
<%@taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<%@taglib prefix="my" tagdir="/WEB-INF/tags"%>
<%-- CONTENT --%>
<my:page>
<jsp:attribute name="script">
<script type="text/javascript" src="${lib}/jquery.spring-friendly/jquery.spring-friendly.js"></script>
</jsp:attribute>
<jsp:attribute name="pageHeaderTitle">Platform Certificates</jsp:attribute>
<jsp:body>
<!-- text and icon resource variables -->
<c:set var="endorsementIcon" value="${icons}/ic_vpn_key_black_24dp.png"/>
<div class="aca-input-box-header">
<form:form method="POST" action="${portal}/certificate-request/platform-credentials/upload" enctype="multipart/form-data">
Platform Credentials
<my:file-chooser id="platformCredentialEditor" label="Import Platform Credentials">
<input id="importFile" type="file" name="file" multiple="multiple" />
</my:file-chooser>
<a href="${portal}/certificate-request/platform-credentials/bulk">
<img src="${icons}/ic_file_download_black_24dp.png" title="Download All Platform Certificates">
</a>
</form:form>
</div>
<br/>
<div class="aca-data-table">
<table id="platformTable" class="display" width="100%">
<thead>
<tr>
<th>Device</th>
<th>Issuer</th>
<th>Type</th>
<th>Manufacturer</th>
<th>Model</th>
<th>Version</th>
<th>Board SN</th>
<th>Valid (begin)</th>
<th>Valid (end)</th>
<th>Endorsement</th>
<th>Options</th>
</tr>
</thead>
</table>
</div>
<script>
$(document).ready(function() {
var url = pagePath +'/list';
var columns = [
{
data: 'device.name',
render: function (data, type, full, meta) {
// if there's a device, display its name, otherwise
// display nothing
if (full.device) {
// TODO render a link to a device details page,
// passing the device.id
return full.device.name;
}
return '';
}
},
{data: 'issuer'},
{
data: 'credentialType',
render: function (data, type, full, meta) {
if (full.platformType !== '') {
return full.platformType;
} else {
return full.credentialType;
}
}
},
{data: 'manufacturer'},
{data: 'model'},
{data: 'version'},
{data: 'platformSerial'},
{
data: 'beginValidity',
searchable:false,
render: function (data, type, full, meta) {
return formatDateTime(full.beginValidity);
}
},
{
data: 'endValidity',
searchable:false,
render: function (data, type, full, meta) {
return formatDateTime(full.endValidity);
}
},
{
data: 'id',
orderable: false,
searchable:false,
render: function (data, type, full, meta) {
//Display endorsement credential
if(full.endorsementCredential === null) return '';
var html = '';
var id = full.endorsementCredential.id;
html = certificateDetailsLink('endorsement', id, false) +'&nbsp;';
return html;
}
},
{
data: 'id',
orderable: false,
searchable:false,
render: function(data, type, full, meta) {
// Set up a delete icon with link to handleDeleteRequest().
// sets up a hidden input field containing the ID which is
// used as a parameter to the REST POST call to delete
var html = '';
html += certificateDetailsLink('platform', full.id, true);
html += certificateDownloadLink(full.id, pagePath);
html += certificateDeleteLink(full.id, pagePath);
return html;
}
}
];
//Set data tables
setDataTables("#platformTable", url, columns);
});
</script>
</jsp:body>
</my:page>

View File

@ -0,0 +1,78 @@
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<%-- JSP TAGS --%>
<%@taglib prefix="c" uri="jakarta.tags.core" %>
<%@taglib prefix="spring" uri="http://www.springframework.org/tags"%>
<%@taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<%@taglib prefix="my" tagdir="/WEB-INF/tags"%>
<%-- CONTENT --%>
<my:page>
<jsp:attribute name="script">
<script type="text/javascript" src="${lib}/jquery.spring-friendly/jquery.spring-friendly.js"></script>
</jsp:attribute>
<jsp:attribute name="pageHeaderTitle">Reference Integrity Manifests</jsp:attribute>
<jsp:body>
<!-- text and icon resource variables -->
<div class="aca-input-box-header">
<form:form method="POST" action="${portal}/reference-manifests/upload" enctype="multipart/form-data">
Reference Integrity Manifests
<my:file-chooser id="referenceManifestsEditor" label="Import RIMs">
<input id="importFile" type="file" name="file" multiple="multiple" />
</my:file-chooser>
<a href="${portal}/reference-manifests/bulk">
<img src="${icons}/ic_file_download_black_24dp.png" title="Download All RIMs">
</a>
</form:form>
</div>
<br/>
<div class="aca-data-table">
<table id="referenceManifestTable" class="display" width="100%">
<thead>
<tr>
<th>Tag ID</th>
<th>Type</th>
<th>Manufacturer</th>
<th>Model</th>
<th>Version</th>
<th>Options</th>
</tr>
</thead>
</table>
</div>
<script>
$(document).ready(function() {
var url = pagePath +'/list';
var columns = [
{data: 'tagId'},
{data: 'rimType'},
{data: 'platformManufacturer'},
{data: 'platformModel'},
{data: 'swidTagVersion'},
{
data: 'id',
orderable: false,
searchable: false,
render: function(data, type, full, meta) {
// Set up a delete icon with link to handleDeleteRequest().
// sets up a hidden input field containing the ID which is
// used as a parameter to the REST POST call to delete
var html = '';
html += rimDetailsLink(full.id);
html += rimDownloadLink(full.id, pagePath);
html += rimDeleteLink(full.id, pagePath);
return html;
}
}
];
//Set data tables
setDataTables("#referenceManifestTable", url, columns);
});
</script>
</jsp:body>
</my:page>

View File

@ -0,0 +1,75 @@
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<%-- JSP TAGS --%>
<%@taglib prefix="c" uri="jakarta.tags.core" %>
<%@taglib prefix="spring" uri="http://www.springframework.org/tags"%>
<%@taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<%@taglib prefix="my" tagdir="/WEB-INF/tags"%>
<%-- CONTENT --%>
<my:page>
<jsp:attribute name="script">
<script type="text/javascript" src="${lib}/jquery.spring-friendly/jquery.spring-friendly.js"></script>
</jsp:attribute>
<jsp:attribute name="pageHeaderTitle">RIM Database</jsp:attribute>
<jsp:body>
<br/>
<div class="aca-data-table">
<table id="digestValueTable" class="display" width="100%">
<thead>
<tr>
<th>Manufacturer</th>
<th>Model</th>
<th>Event Type</th>
<th>PCR Index</th>
<th>Digest Value</th>
<th>Base RIM</th>
<th>Support RIM</th>
</tr>
</thead>
</table>
</div>
<script>
$(document).ready(function() {
var url = pagePath +'/list';
var columns = [
{data: 'manufacturer',
orderable: true,
searchable:false},
{data: 'model',
orderable: false,
searchable:false},
{data: 'eventType',
orderable: false,
searchable:false,},
{data: 'pcrIndex',
orderable: true,
searchable:false},
{data: 'digestValue',
orderable: false,
searchable:false},
{data: 'baseRimId',
orderable: false,
searchable: false,
render: function(data, type, full, meta) {
return rimDetailsLink(full.baseRimId);
}
},
{data: 'supportRimId',
orderable: false,
searchable: false,
render: function(data, type, full, meta) {
return rimDetailsLink(full.supportRimId);
}
}
];
//Set data tables
setDataTables("#digestValueTable", url, columns);
});
</script>
</jsp:body>
</my:page>

View File

@ -0,0 +1,653 @@
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<%-- JSP TAGS--%>
<%@taglib prefix="c" uri="jakarta.tags.core" %>
<%@taglib prefix="spring" uri="http://www.springframework.org/tags"%>
<%@taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<%@taglib prefix="my" tagdir="/WEB-INF/tags"%>
<%--CONTENT--%>
<my:page>
<jsp:attribute name="style">
<link type="text/css" rel="stylesheet" href="${common}/certificate_details.css"/>
<link type="text/css" rel="stylesheet" href="${common}/rim_details.css"/>
</jsp:attribute>
<jsp:attribute name="pageHeaderTitle">
<c:choose>
<c:when test="${initialData.rimType=='Measurement'}">
<c:if test="${initialData.validationResult=='PASS'}">
TCG Log events
</c:if>
<c:if test="${initialData.validationResult=='FAIL'}">
TCG Log event(s) not found in the RIM DB
</c:if>
</c:when>
<c:otherwise>
${initialData.rimType} Reference Integrity Manifest
<a href="${portal}/reference-manifests/download?id=${param.id}">
<img src="${icons}/ic_file_download_black_24dp.png" title="Download ${initialData.rimType} RIM">
</a>
</c:otherwise>
</c:choose>
</jsp:attribute>
<jsp:body>
<c:set var="passIcon" value="${icons}/ic_checkbox_marked_circle_black_green_24dp.png"/>
<c:set var="failIcon" value="${icons}/ic_error_red_24dp.png"/>
<c:set var="signatureValidText" value="Signature valid!"/>
<c:set var="signatureInvalidText" value="Signature not valid!"/>
<c:set var="supportRimHashValidText" value="Support RIM hash valid!"/>
<c:set var="supportRimHashInvalidText" value="Support RIM hash not valid!"/>
<div id="certificate-details-page" class="container-fluid">
<c:choose>
<c:when test="${initialData.rimType=='Support' || (initialData.rimType=='Measurement' && initialData.validationResult=='PASS')}">
<div class="row">
<div class="col-md-1 col-md-offset-1"><span class="colHeader">Additional<br />RIM Info</span></div>
<div class="col col-md-8">
<c:choose>
<c:when test="${not empty initialData.associatedRim}">
<a href="${portal}/rim-details?id=${initialData.associatedRim}">
${initialData.tagId}
</a>
<c:if test="${not empty initialData.hostName}">
<div>Device:&nbsp;<span>${initialData.hostName}</span></div>
</c:if>
<c:if test="${not empty initialData.supportId}">
<div>Support:&nbsp;<span><a href="${portal}/rim-details?id=${initialData.supportId}">${initialData.supportFilename}</a></span>
</div>
</c:if>
</c:when>
<c:otherwise>
<div class="component col col-md-10" style="color: red; padding-left: 20px">RIM not uploaded from the ACA RIM Page</div>
</c:otherwise>
</c:choose>
</div>
</div>
<c:if test="${not initialData.swidBase}">
<div class="row">
<div class="col-md-1 col-md-offset-1">
<span class="colHeader">RIM Type</span>
</div>
<div id="baseRim" class="col col-md-8">
<c:if test="${initialData.swidCorpus}">
<div>SWID Corpus</div>
</c:if>
<c:if test="${initialData.swidPatch}">
<div>SWID Patch</div>
</c:if>
<c:if test="${initialData.swidSupplemental}">
<div>SWID Supplemental</div>
</c:if>
</div>
</div>
</c:if>
<div class="row">
<div class="col-md-1 col-md-offset-1">
<span class="colRimHeader">
<a role="button" data-toggle="collapse" class="collapsed" href="#eventOptions"
aria-expanded="true" data-placement="top" aria-controls="eventOptions">
Event Summary
</a>
</span>
</div>
<div id="eventsCol" class="col col-md-8">
<div id="eventOptions" class="collapse" class="collapsed" aria-expanded="false">
<ul>
<c:choose>
<c:when test="${initialData.rimType=='Support'}">
<li>This Support RIM file covers the following critical items:</li>
</c:when>
<c:otherwise>
<li>This Event Log file covers the following critical items:</li>
</c:otherwise>
</c:choose>
<ul>
<c:if test="${initialData.crtm || initialData.bootManager || initialData.osLoader || initialData.osKernel}">
<li>PC Client Boot path</li>
</c:if>
<ul>
<c:if test="${initialData.crtm}">
<li>Software Core Root of Trust for Measurement (SRTM)</li>
</c:if>
<c:if test="${initialData.bootManager}">
<li>Boot Manager</li>
</c:if>
<c:if test="${initialData.osLoader}">
<li>OS Loader</li>
</c:if>
<c:if test="${initialData.osKernel}">
<li>OS Kernel</li>
</c:if>
</ul>
<c:if test="${initialData.acpiTables || initialData.smbiosTables || initialData.gptTable || initialData.defaultBootDevice}">
<li>Device Configuration</li>
</c:if>
<ul>
<c:if test="${initialData.acpiTables}">
<li>ACPI Tables</li>
</c:if>
<c:if test="${initialData.smbiosTables}">
<li>SMBIOS Tables</li>
</c:if>
<c:if test="${initialData.gptTable}">
<li>GPT Table</li>
</c:if>
<c:if test="${initialData.bootOrder}">
<li>Boot Order</li>
</c:if>
<c:if test="${initialData.defaultBootDevice}">
<li>Default boot device</li>
</c:if>
</ul>
<c:if test="${initialData.secureBoot || initialData.pk || initialData.kek || initialData.sigDb || initialData.forbiddenDbx}">
<li>Secure Boot Variables</li>
</c:if>
<ul>
<c:if test="${initialData.secureBoot}">
<li>Secure Boot Enabled</li>
</c:if>
<c:if test="${initialData.pk}">
<li>Platform Key (PK)</li>
</c:if>
<c:if test="${initialData.kek}">
<li>Key Exchange Key (KEK)</li>
</c:if>
<c:if test="${initialData.sigDb}">
<li>Signature Database (db)</li>
</c:if>
<c:if test="${initialData.forbiddenDbx}">
<li>Forbidden Signatures Database (dbx)</li>
</c:if>
</ul>
</ul>
</ul>
<ul>
<c:choose>
<c:when test="${initialData.rimType=='Support'}">
<li>This Support RIM file covers the following critical items:</li>
</c:when>
<c:otherwise>
<li>This Event Log file covers the following critical items:</li>
</c:otherwise>
</c:choose>
<ul>
<c:if test="${not initialData.crtm || not initialData.bootManager || not initialData.osLoader || not initialData.osKernel}">
<li>PC Client Boot path</li>
</c:if>
<ul>
<c:if test="${not initialData.crtm}">
<li>Software Core Root of Trust for Measurement (SRTM)</li>
</c:if>
<c:if test="${not initialData.bootManager}">
<li>Boot Manager</li>
</c:if>
<c:if test="${not initialData.osLoader}">
<li>OS Loader</li>
</c:if>
<c:if test="${not initialData.osKernel}">
<li>OS Kernel</li>
</c:if>
</ul>
<c:if test="${not initialData.acpiTables || not initialData.smbiosTables || not initialData.gptTable || not initialData.bootOrder || not initialData.defaultBootDevice}">
<li>Device Configuration</li>
</c:if>
<ul>
<c:if test="${not initialData.acpiTables}">
<li>ACPI Tables</li>
</c:if>
<c:if test="${not initialData.smbiosTables}">
<li>SMBIOS Tables</li>
</c:if>
<c:if test="${not initialData.gptTable}">
<li>GPT Table</li>
</c:if>
<c:if test="${not initialData.bootOrder}">
<li>Boot Order</li>
</c:if>
<c:if test="${not initialData.defaultBootDevice}">
<li>Default boot device</li>
</c:if>
</ul>
<c:if test="${not initialData.secureBoot || not initialData.pk || not initialData.kek || not initialData.sigDb || not initialData.forbiddenDbx}">
<li>Secure Boot Variables</li>
</c:if>
<ul>
<c:if test="${not initialData.secureBoot}">
<li>Secure Boot Enabled</li>
</c:if>
<c:if test="${not initialData.pk}">
<li>Platform Key (PK)</li>
</c:if>
<c:if test="${not initialData.kek}">
<li>Key Exchange Key (KEK)</li>
</c:if>
<c:if test="${not initialData.sigDb}">
<li>Signature Database (db)</li>
</c:if>
<c:if test="${not initialData.forbiddenDbx}">
<li>Forbidden Signatures Database (dbx)</li>
</c:if>
</ul>
</ul>
</ul>
</div>
</div>
</div>
</div>
<div id="tableDivTag">
<input type="text" id="eventInput" onkeyup="eventSearch(null)" placeholder="Search for text..." /><br />
<table id="eventLog">
<thead>
<tr class="header">
<th>Event #</th>
<th>PCR Index</th>
<th style="width: 20%">Event Type</th>
<th>Digest</th>
<th style="width: 50%">Event Content</th>
</tr>
</thead>
<tbody>
<c:if test="${not empty initialData.events}">
<c:set var="count" value="1" scope="page"/>
<c:forEach items="${initialData.events}" var="event">
<c:choose>
<c:when test="${event.isError()}">
<tr style="background: tomato">
</c:when>
<c:otherwise>
<tr>
</c:otherwise>
</c:choose>
<td style="width: 75px">${count}</td>
<td class="pcrCell">PCR${event.getPcrIndex()}</td>
<td>${event.getEventTypeStr()}</td>
<td class="digestCell">${event.getEventDigestStr()}</td>
<td title="${event.getEventContentStr()}"><div style="height: 50px; overflow: auto">${event.getEventContentStr()}</div></td>
</tr>
<c:set var="count" value="${count + 1}" scope="page"/>
</c:forEach>
</c:if>
</tbody>
</table>
</div>
<div class="col-md-a col-md-offset-1"><span class="colHeader">${initialData.events.size()} entries</span></div>
</c:when>
<c:when test="${initialData.rimType=='Measurement' && initialData.validationResult=='FAIL'}">
<div style="display: inline">
<div class="row">
<div class="col-md-1 col-md-offset-1"><span class="colHeader">Base/Support</span></div>
<div id="measurements" class="col col-md-8">
<div>Download Measurement:&nbsp;
<span>
<a href="${portal}/reference-manifests/download?id=${param.id}">
BIOS Measurements
</a>
</span>
</div>
<c:if test="${not empty initialData.hostName}">
<div>Device:&nbsp;<span>${initialData.hostName}</span>
</div>
</c:if>
<c:if test="${not empty initialData.tagId}">
<div>Base:&nbsp;<span><a href="${portal}/rim-details?id=${initialData.associatedRim}">${initialData.tagId}</a></span>
</div>
</c:if>
<c:if test="${not empty initialData.supportId}">
<div>Support:&nbsp;<span><a href="${portal}/rim-details?id=${initialData.supportId}">${initialData.supportFilename}</a></span>
</div>
</c:if>
</div>
</div>
<br />
<div class="row" style="margin: auto 260px auto 125px">
<div class="panel panel-default" style="flex: 1">
<div class="panel-heading">Client Log</div>
<c:if test="${not empty initialData.livelogEvents}">
<c:set var="iterator" value="0" scope="page"/>
<c:forEach items="${initialData.livelogEvents}" var="lEvent">
<div>
<div style="display: flex; background: lightgray;">
<div style="display: flex 1; font-weight: bold; margin: auto 1rem auto 1rem">Failed Event Digest:<br />
</div>
<div style="display: flex 2; margin: 2px auto 2px 25px">
<span class="mappedData">PCR Index:</span> ${lEvent.getPcrIndex()}<br />
<span class="mappedData">Digest:</span> ${lEvent.getEventDigestStr()}<br />
<span class="mappedData">Event Content:</span> ${lEvent.getEventContentStr()}
</div>
</div>
</div>
<div style="display: flex;">
<div class="mappedButton">
Expected Events from RIM DB:<br />
<span style="word-wrap: break-word"><a role="button" data-toggle="collapse" href="#eventContent${iterator}">${lEvent.getEventTypeString()}</a></span>
</div>
<div id="eventContent${iterator}" class="panel-collapse collapse in" style="flex: 2">
<c:forEach items="${initialData.eventTypeMap}" var="mappedDigest">
<c:if test="${mappedDigest.key == lEvent.getEventDigestStr()}">
<c:set var="event" value="${mappedDigest.value}" scope="page"/>
<c:forEach items="${mappedDigest.value}" var="event">
<div class="mappedOverhead">
<div><span class="mappedData">PCR Index:</span> ${event.getPcrIndex()}</div>
<div><span class="mappedData">Digest:</span> ${event.getEventDigestStr()}</div>
<div><span class="mappedData">Event Content:</span> ${event.getEventContentStr()}</div>
</div>
</c:forEach>
</c:if>
</c:forEach>
</div>
</div>
<c:set var="iterator" value="${iterator+1}" scope="page"/>
</c:forEach>
</c:if>
</div>
</div>
</div>
</c:when>
<c:when test="${initialData.rimType=='Base'}">
<div class="row">
<div class="col-md-1 col-md-offset-1"><span class="colHeader">Software Identity</span></div>
<div id="softwareIdentity" class="col col-md-8">
<div>SWID Name:&nbsp;<span>${initialData.swidName}</span></div>
<div>SWID Version:&nbsp;<span>${initialData.swidVersion}</span></div>
<div>SWID Tag ID:&nbsp;<span>${initialData.swidTagId}</span></div>
<div>SWID Tag Version:&nbsp;<span>${initialData.swidTagVersion}</span></div>
<c:if test="${initialData.swidCorpus}">
<div>SWID Corpus:&nbsp;<span><img src="${icons}/ic_checkbox_marked_circle_black_green_24dp.png" title="Corpus Flag"></span>
</div>
</c:if>
<c:if test="${initialData.swidPatch}">
<div>SWID Patch:&nbsp;<span><img src="${icons}/ic_checkbox_marked_circle_black_green_24dp.png" title="Patch Flag"></span>
</div>
</c:if>
<c:if test="${initialData.swidSupplemental}">
<div>SWID Supplemental:&nbsp;<span><img src="${icons}/ic_checkbox_marked_circle_black_green_24dp.png" title="Supplemental Flag"></span>
</div>
</c:if>
</div>
</div>
<div class="row">
<div class="col-md-1 col-md-offset-1"><span class="colHeader">Entity</span></div>
<div id="entity" class="col col-md-8">
<div>Entity Name:&nbsp;<span>${initialData.entityName}</span></div>
<c:if test="${not empty initialData.entityRegId}">
<div>Entity Reg ID:&nbsp;<span>${initialData.entityRegId}</span></div>
</c:if>
<div>Entity Role:&nbsp;<span>${initialData.entityRole}</span></div>
<div>Entity Thumbprint:&nbsp;<span>${initialData.entityThumbprint}</span></div>
</div>
</div>
<div class="row">
<div class="col-md-1 col-md-offset-1"><span class="colHeader">Link</span></div>
<div id="link" class="col col-md-8">
<c:if test="${not empty initialData.linkHref}">
<div>
<span>
<c:choose>
<c:when test="${not empty initialData.linkHrefLink}">
<a href="${portal}/rim-details?id=${initialData.linkHrefLink}" rel="${initialData.linkRel}">${initialData.linkHref}</a>
</c:when>
<c:otherwise>
<a href="${initialData.linkHref}" rel="${initialData.linkRel}">${initialData.linkHref}</a>
</c:otherwise>
</c:choose>
</span>
</div>
<div>Rel:&nbsp;<span>${initialData.linkRel}</span>
</div>
</c:if>
</div>
</div>
<div class="row">
<div class="col-md-1 col-md-offset-1"><span class="colHeader">Meta</span></div>
<div id="link" class="col col-md-8">
<div>Platform Manufacturer ID:&nbsp;<span>${initialData.platformManufacturerId}</span></div>
<div>Platform Manufacturer:&nbsp;<span>${initialData.platformManufacturer}</span></div>
<div>Platform Model:&nbsp;<span>${initialData.platformModel}</span></div>
<c:if test="${not empty initialData.platformVersion}">
<div>Platform Version:&nbsp;<span>${initialData.platformVersion}</span></div>
</c:if>
<div>Colloquial Version:&nbsp;<span>${initialData.colloquialVersion}</span></div>
<div>Edition:&nbsp;<span>${initialData.edition}</span></div>
<div>Product:&nbsp;<span>${initialData.product}</span></div>
<div>Revision:&nbsp;<span>${initialData.revision}</span></div>
<c:if test="${not empty initialData.payloadType}">
<div>Payload Type:&nbsp;<span>${initialData.payloadType}</span></div>
</c:if>
<div>Binding Spec:&nbsp;<span>${initialData.bindingSpec}</span></div>
<div>Binding Spec Version:&nbsp;<span>${initialData.bindingSpecVersion}</span></div>
<c:if test="${not empty initialData.pcUriGlobal}">
<div>PC URI Global:&nbsp;<span>${initialData.pcUriGlobal}</span></div>
</c:if>
<c:if test="${not empty initialData.pcUriLocal}">
<div>PC URI Local:&nbsp;<span>${initialData.pcUriLocal}</span></div>
</c:if>
<c:choose>
<c:when test="${not empty initialData.rimLinkId}">
<div>Rim Link Hash:&nbsp;<span><a href="${portal}/rim-details?id=${initialData.rimLinkId}">${initialData.rimLinkHash}</a></span>
</c:when>
<c:otherwise>
<div>Rim Link Hash:&nbsp;<span>${initialData.rimLinkHash}</span>
</c:otherwise>
</c:choose>
<c:if test="${not empty initialData.rimLinkHash}">
<span>
<c:choose>
<c:when test="${initialData.linkHashValid}">
<img src="${passIcon}" title="SWID Tag exist.">
</c:when>
<c:otherwise>
<img src="${failIcon}" title="SWID Tag doesn't exist.">
</c:otherwise>
</c:choose>
</span>
</c:if>
</div>
</div>
</div>
<div class="row">
<div class="col-md-1 col-md-offset-1"><span class="colHeader">Payload/Support RIM(s)</span></div>
<div id="platformConfiguration" class="col col-md-8">
<div class="panel panel-default">
<div class="panel-heading" role="tab" id="headingOne">
<h4 class="panel-title">
<a role="button" data-toggle="collapse" data-parent="#platformConfiguration" class="collapsed"
href="#directorycollapse" aria-expanded="true" aria-controls="directorycollapse">
Directory
</a>
</h4>
</div>
<div id="directorycollapse" class="panel-collapse collapse in" role="tabpanel" aria-labelledby="headingOne" aria-expanded="true">
<div class="panel-body">
<div class="panel-heading" role="tab" id="headingThree">
<h3 class="panel-title">
<a role="button" data-toggle="collapse" data-parent="#directorycollapse" class="collapsed"
href="#filescollapse" aria-expanded="false" aria-controls="filescollapse">
Files
</a>
</h3>
</div>
<div id="filescollapse" class="panel-collapse collapse" role="tabpanel" aria-labelledby="headingThree" aria-expanded="true">
<c:if test="${not empty initialData.swidFiles}">
<div id="componentIdentifier" class="row">
<c:forEach items="${initialData.swidFiles}" var="resource">
<div class="component col col-md-10" style="padding-left: 20px">
<div class="panel panel-default">
<div class="panel-heading">
<span data-toggle="tooltip" data-placement="top" title="Resource File">
<c:choose>
<c:when test="${not empty initialData.associatedRim}">
<a href="${portal}/rim-details?id=${initialData.associatedRim}">${resource.getName()}</a>
<c:choose>
<c:when test="${not empty initialData.supportRimHashValid}">
<img src="${passIcon}" title="${supportRimHashValidText}"/>
</c:when>
<c:otherwise>
<img src="${failIcon}" title="${supportRimHashInvalidText}"/>
</c:otherwise>
</c:choose>
</c:when>
<c:otherwise>
${resource.getName()}
</c:otherwise>
</c:choose>
</span>
</div>
<div class="component col col-md-10">
<span class="fieldHeader">File Size:</span>
<span class="fieldValue">${resource.getSize()}</span><br/>
<span class="fieldHeader">Hash:</span>
<span class="fieldValue" style="overflow-wrap: break-word">${resource.getHashValue()}</span><br/>
<c:if test="${not empty resource.getRimFormat()}">
<span class="fieldHeader">RIM Format:</span>
<span class="fieldValue">${resource.getRimFormat()}</span><br/>
</c:if>
<c:if test="${not empty resource.getRimType()}">
<span class="fieldHeader">RIM Type:</span>
<span class="fieldValue">${resource.getRimType()}</span><br/>
</c:if>
<c:if test="${not empty resource.getRimUriGlobal()}">
<span class="fieldHeader">URI Global:</span>
<span class="fieldValue">${resource.getRimUriGlobal()}</span><br/>
</c:if>
</div>
<c:choose>
<c:when test="${not empty initialData.pcrList}">
<div class="component col col-md-10">
<div class="panel-body">
<div class="component" role="tab" id="pcrValues">
<a role="button" data-toggle="collapse" data-parent="#directorycollapse" class="collapsed"
href="#pcrscollapse" aria-expanded="false" aria-controls="pcrscollapse">
Expected PCR Values
</a>
</div>
<div id="pcrscollapse" class="panel-collapse collapse" role="tabpanel" aria-labelledby="headingThree" aria-expanded="true">
<div>
<c:set var="count" value="0" scope="page"/>
<c:forEach items="${initialData.pcrList}" var="pcrValue">
<div id="componentIdentifier" class="row">
<div>
<span>PCR ${count} - </span>
<span style="overflow-wrap: break-word">${pcrValue}</span>
</div>
</div>
<c:set var="count" value="${count + 1}" scope="page"/>
</c:forEach>
</div>
</div>
</div>
</div>
</c:when>
<c:otherwise>
<c:if test="${not initialData.swidPatch and not initialData.swidSupplemental}">
<div class="component col col-md-10" style="color: red; padding-left: 20px">Support RIM file named ${resource.getName()} was not imported via the Reference Integrity Manifest page.</div>
</c:if>
</c:otherwise>
</c:choose>
</div>
</div>
</div>
</c:forEach>
</div>
</c:if>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-1 col-md-offset-1"><span class="colHeader">Signature</span></div>
<div id="signature" class="col col-md-8">
<div>Validity:&nbsp;<span>
<c:choose>
<c:when test="${initialData.signatureValid}">
<img src="${passIcon}" title="${signatureValidText}"/>
</c:when>
<c:otherwise>
<img src="${failIcon}" title="${signatureInvalidText}"/>
</c:otherwise>
</c:choose>
</span>
</div>
<div>
<span>
<c:if test="${not empty initialData.issuerID}">
<div><a href="${portal}/certificate-details?id=${initialData.issuerID}&type=certificateauthority">Signing certificate</a></div>
</c:if>
</span>
</div>
<div>
<span>
<c:if test="${not empty initialData.skID}">
<div>Subject Key Identifier: ${initialData.skID}</div>
</c:if>
</span>
</div>
</div>
</div>
</c:when>
<c:otherwise>
</c:otherwise>
</c:choose>
</div>
</div>
</div>
<script>
function eventSearch(txtInput) {
// Declare variables
var input, filter, table, tr, td, i, txtValue, txtFound;
if (txtInput === null) {
input = document.getElementById("eventInput");
filter = input.value.toUpperCase();
} else {
filter = txtInput;
}
table = document.getElementById("eventLog");
tr = table.getElementsByTagName("tr");
// Loop through all table rows, and hide those who don't match the search query
for (i = 0; i < tr.length; i++) {
txtFound = true;
tds = tr[i].getElementsByTagName("td");
for (j = 0; j < tds.length; j++) {
td = tds[j];
if (td) {
txtValue = td.textContent || td.innerText;
if (txtValue.toUpperCase().indexOf(filter) > -1) {
txtFound = true;
break;
} else {
txtFound = false;
}
}
}
if (txtFound) {
tr[i].style.display = "";
} else {
tr[i].style.display = "none";
}
}
}
window.onload = function () {
// Constant retrieved from server-side via JSP
var maxRows = 11;
var table = document.getElementById('eventLog');
var wrapper = table.parentNode;
var rowsInTable = table.rows.length;
var height = 0;
if (rowsInTable > maxRows) {
for (var i = 0; i < maxRows; i++) {
height += table.rows[i].clientHeight;
}
wrapper.style.height = height + "px";
}
}
</script>
</jsp:body>
</my:page>

View File

@ -0,0 +1,155 @@
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<%-- JSP TAGS --%>
<%@taglib prefix="c" uri="jakarta.tags.core" %>
<%@taglib prefix="spring" uri="http://www.springframework.org/tags"%>
<%@taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<%@taglib prefix="my" tagdir="/WEB-INF/tags"%>
<%-- CONTENT --%>
<my:page>
<jsp:attribute name="script">
<script type="text/javascript" src="${lib}/jquery.spring-friendly/jquery.spring-friendly.js"></script>
</jsp:attribute>
<jsp:attribute name="pageHeaderTitle">Trust Chain Management</jsp:attribute>
<jsp:body>
<span class="aca-input-box-header">
HIRS Attestation CA Certificate
</span>
<my:details-viewer id="aca-cert-viewer" label="HIRS Attestation CA Certificate">
<div class="container-fluid">
<div class="row">
<div class="col-md-2 col-md-offset-1"><span class="colHeader">Issuer</span></div>
<div id="issuer" class="col col-md-8">
<!-- Display the issuer, and provide a link to the issuer details if provided -->
<c:choose>
<c:when test="${not empty acaCertData.issuerID}">
<a href="${portal}/certificate-details?id=${acaCertData.issuerID}&type=certificateauthority">
${acaCertData.issuer}
</a>
</c:when>
<c:otherwise>
${acaCertData.issuer}
</c:otherwise>
</c:choose>
</div>
</div>
<c:if test="${not empty acaCertData.subject}">
<div class="row">
<div class="col-md-2 col-md-offset-1"><span class="colHeader">Subject</span></div>
<div id="subject" class="col col-md-8">${acaCertData.subject}</div>
</div>
</c:if>
<div class="row">
<div class="col-md-2 col-md-offset-1"><span class="colHeader">Serial Number</span></div>
<div id="serialNumber" class="col col-md-8">${acaCertData.serialNumber}</div>
</div>
<div class="row">
<div class="col-md-2 col-md-offset-1"><span class="colHeader">Validity</span></div>
<div id="validity" class="col col-md-8">
<div>Not Before:&nbsp;<span>${acaCertData.beginValidity}</span></div>
<div>Not After:&nbsp;<span>${acaCertData.endValidity}</span></div>
</div>
</div>
<div class="row">
<div class="col-md-2 col-md-offset-1"><span class="colHeader">Signature</span></div>
<div id="signature" class="col col-md-8"></div>
</div>
<c:if test="${not empty acaCertData.encodedPublicKey}">
<div class="row">
<div class="col-md-2 col-md-offset-1"><span class="colHeader">Public Key</span></div>
<div id="encodedPublicKey" class="col col-md-8"></div>
</div>
</c:if>
<div class="spacer"></div>
</div>
</my:details-viewer>
<a href="${portal}/certificate-request/trust-chain/download-aca-cert">
<img src="${baseURL}/images/icons/ic_file_download_black_24dp.png" title="Download ACA Certificate">
</a>
<div class="aca-input-box-header">
<form:form method="POST" action="${portal}/certificate-request/trust-chain/upload" enctype="multipart/form-data">
Trust Chain CA Certificates
<my:file-chooser id="tc-editor" label="Import Trust Chain Certificates">
<input id="importFile" type="file" name="file" multiple="multiple" />
</my:file-chooser>
<a href="${portal}/certificate-request/trust-chain/bulk">
<img src="${icons}/ic_file_download_black_24dp.png" title="Download All Trust Chain Certificates">
</a>
</form:form>
</div>
<br/>
<div class="aca-data-table">
<table id="trustChainTable" class="display" width="100%">
<thead>
<tr>
<th>Issuer</th>
<th>Subject</th>
<th>Valid (begin)</th>
<th>Valid (end)</th>
<th>Options</th>
</tr>
</thead>
</table>
</div>
<script>
$(document).ready(function() {
var url = pagePath +'/list';
var signature = ${acaCertData.signature};
//Format validity time
$("#validity span").each(function(){
$(this).text(formatDateTime($(this).text()));
});
//Convert byte array to string
$("#signature").html(byteToHexString(signature));
<c:if test="${not empty acaCertData.encodedPublicKey}">
//Change publick key byte to hex
var publicKey = ${acaCertData.encodedPublicKey};
$("#encodedPublicKey").html(byteToHexString(publicKey));
</c:if>
var columns = [
{data: 'issuer'},
{data: 'subject'},
{
data: 'beginValidity',
searchable:false,
render: function (data, type, full, meta) {
return formatDateTime(full.beginValidity);
}
},
{
data: 'endValidity',
searchable:false,
render: function (data, type, full, meta) {
return formatDateTime(full.endValidity);
}
},
{
data: 'id',
orderable: false,
searchable:false,
render: function(data, type, full, meta) {
// Set up a delete icon with link to handleDeleteRequest().
// sets up a hidden input field containing the ID which is
// used as a parameter to the REST POST call to delete
var html = '';
html += certificateDetailsLink('certificateauthority', full.id, true);
html += certificateDownloadLink(full.id, pagePath);
html += certificateDeleteLink(full.id, pagePath);
return html;
}
}
];
//Set data tables
setDataTables("#trustChainTable", url, columns);
});
</script>
</jsp:body>
</my:page>

View File

@ -0,0 +1,256 @@
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<%-- JSP TAGS --%>
<%@taglib prefix="c" uri="jakarta.tags.core" %>
<%@taglib prefix="spring" uri="http://www.springframework.org/tags"%>
<%@taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<%@taglib prefix="my" tagdir="/WEB-INF/tags"%>
<%-- CONTENT --%>
<my:page>
<jsp:attribute name="script">
<script type="text/javascript" src="${lib}/jquery.spring-friendly/jquery.spring-friendly.js"></script>
</jsp:attribute>
<jsp:attribute name="pageHeaderTitle">Validation Reports</jsp:attribute>
<jsp:body>
<!-- text and icon resource variables -->
<c:set var="passIcon" value="${icons}/ic_checkbox_marked_circle_black_green_24dp.png"/>
<c:set var="failIcon" value="${icons}/ic_error_red_24dp.png"/>
<c:set var="errorIcon" value="${icons}/ic_error_black_24dp.png"/>
<c:set var="unknownIcon" value="${icons}/ic_questionmark_circle_orange_24dp.png"/>
<c:set var="passText" value="Validation Passed"/>
<c:set var="failText" value="Validation Failed"/>
<c:set var="errorText" value="Validation Error"/>
<c:set var="unknownText" value="Unknown Validation Status"/>
<form:form id="download" method="POST" action="${portal}/validation-reports/download">
Download Validation Reports
<my:download-info id="validationReportsDownload" label="Download Validation Reports">
<label>Company<input id="company" type="text" pattern="^\w*$"
title="Letters, numbers, and spaces only" name="company" /></label>
<label>Contract #<input id="contract" type="text" pattern="^\w*$"
title="Letters, numbers, and spaces only" name="contract" /></label>
<br>
<label>Date range start<input id="dateStart" type="date" name="dateStart" /></label>
<label>Date range end<input id="dateEnd" type="date" name="dateEnd" /></label>
</my:download-info>
</form:form>
<div class="aca-data-table">
<table id="reportTable" class="display" width="100%">
<thead>
<tr>
<th rowspan="2">Result</th>
<th rowspan="2">Timestamp</th>
<th rowspan="2">Device</th>
<th colspan="3">Credential Validations</th>
</tr>
<tr>
<th style="text-align:center">Endorsement</th>
<th style="text-align:center">Platform</th>
<th style="text-align:center">Firmware</th>
</tr>
</thead>
</table>
</div>
<script>
$(document).ready(function () {
var url = portal + '/validation-reports/list';
var columns = [
{
data: 'overallValidationResult',
searchable: false,
render: function (data, type, full, meta) {
var html = '';
var unknownStatus = '<img class="icon" src="${unknownIcon}" title="${unknownText}"/>';
// create status icon
var result = full.overallValidationResult;
var ovallMessage = full.message;
if (result) {
switch (result) {
case "PASS":
html += '<img src="${passIcon}" title="${passText}"/>';
break;
case "FAIL":
html += '<img src="${failIcon}" title="' + ovallMessage + '"/>';
break;
case "ERROR":
html += '<img src="${errorIcon}" title="' + ovallMessage + '"/>';
break;
default:
html += unknownStatus;
break;
}
} else {
html += unknownStatus;
}
return html;
}
},
{
// Note: DB column is create_time, while the
// JSON property / java property is createTime. Need to sort
// on the field createTime, but the column's
// date source is create_time.
data: 'create_time',
name: 'createTime',
searchable: false,
render: function (data, type, full, meta) {
return formatDateTime(full.createTime);
}
},
{
// TODO render a link to a device details page,
// passing the device.id
data: 'device.name'
},
{
data: 'id',
searchable: false,
orderable: false,
render: function (data, type, full, meta) {
return getValidationDisplayHtml(full, "ENDORSEMENT_CREDENTIAL")
}
},
{
data: 'id',
searchable: false,
orderable: false,
render: function (data, type, full, meta) {
return getValidationDisplayHtml(full, "PLATFORM_CREDENTIAL")
}
},
{
data: 'id',
searchable: false,
orderable: false,
render: function (data, type, full, meta) {
return getValidationDisplayHtml(full, "FIRMWARE")
}
}
];
//Set data tables
var dataTable = setDataTables("#reportTable", url, columns);
dataTable.order([1, 'desc']).draw(); //order by createTime
});
$("#download").submit(function(e) {
var tableLength = $("#reportTable").rows;
var createTimes = "";
var deviceNames = "";
$('#reportTable tr').not('thead tr').each(function() {
createTimes += $(this).find("td").eq(1).html() + ",";
deviceNames += $(this).find("td").eq(2).html() + ",";
});
createTimes = createTimes.substring(0, createTimes.length - 1);
deviceNames = deviceNames.substring(0, deviceNames.length - 1);
var params = [
{
name: 'createTimes',
value: createTimes
},
{
name: 'deviceNames',
value: deviceNames
}
];
$(this).append($.map(params, function(param) {
return $('<input>', {
type: 'hidden',
name: param.name,
value: param.value
});
}));
});
$(".btn-primary").click(function() {
$("#validationReportsDownload").modal('hide');
});
/**
* Gets HTML to display (icon tag) for the specified validation type.
* If a validation for the requested type is not found, an empty
* string is returned (and no icon will be displayed).
*/
function getValidationDisplayHtml(full, validation_type) {
var html = '';
// loop through all the validations, looking for the one matching
// the validation_type.
for (var i = 0; i < full.validations.length; i++) {
var curValidation = full.validations[i];
var curResult = curValidation.result;
var curMessage = curValidation.message;
if (curValidation.validationType === validation_type) {
var unknownStatus = '<img class="icon" src="${unknownIcon}" title="${unknownText}"/>';
// display appropriate icon based on result
if (curResult) {
// if this validation is associated with a certificate,
// link to the details page
if (curValidation.certificatesUsed.length > 0) {
var certType = '';
switch (validation_type) {
case "PLATFORM_CREDENTIAL":
case "PLATFORM_CREDENTIAL_ATTRIBUTES":
certType = "platform";
break;
case "ENDORSEMENT_CREDENTIAL":
certType = "endorsement";
break;
}
switch (validation_type) {
case "PLATFORM_CREDENTIAL":
case "PLATFORM_CREDENTIAL_ATTRIBUTES":
case "ENDORSEMENT_CREDENTIAL":
html += '<a href="${portal}/certificate-details?id='
+ curValidation.certificatesUsed[0].id
+ '&type=' + certType + '">';
break;
}
}
switch (validation_type) {
case "FIRMWARE":
html += '<a href="${portal}/rim-details?id='
+ curValidation.rimId + '">';
break;
}
switch (curResult) {
case "PASS":
html += '<img src="${passIcon}" title="' + curMessage + '"/>';
break;
case "FAIL":
html += '<img src="${failIcon}" title="' + curMessage + '"/>';
break;
case "ERROR":
html += '<img src="${errorIcon}" title="' + curMessage + '"/>';
break;
default:
html += unknownStatus;
break;
}
// add closing tag for href tag if needed.
if (curValidation.certificatesUsed.length > 0 || curValidation.rimId !== "") {
html += '</a>';
}
} else {
html += unknownStatus;
}
}
}
return html;
}
</script>
</jsp:body>
</my:page>

File diff suppressed because it is too large Load Diff