This is an initial commit with changes that add new classes for digest reference matching.

This commit is contained in:
Cyrus 2021-03-17 10:23:08 -04:00
parent 9134e2ab9d
commit 53cb300063
11 changed files with 214 additions and 262 deletions

View File

@ -12,6 +12,7 @@ import hirs.data.persist.BaseReferenceManifest;
import hirs.data.persist.EventLogMeasurements;
import hirs.data.persist.Device;
import hirs.data.persist.DeviceInfoReport;
import hirs.data.persist.ReferenceDigestRecord;
import hirs.data.persist.ReferenceManifest;
import hirs.data.persist.SupplyChainPolicy;
import hirs.data.persist.SupportReferenceManifest;
@ -28,6 +29,7 @@ import hirs.data.persist.certificate.IssuedAttestationCertificate;
import hirs.data.persist.certificate.PlatformCredential;
import hirs.data.service.DeviceRegister;
import hirs.persist.CertificateManager;
import hirs.persist.ReferenceDigestManager;
import hirs.persist.ReferenceManifestManager;
import hirs.persist.DBManager;
import hirs.persist.DeviceManager;
@ -169,6 +171,7 @@ public abstract class AbstractAttestationCertificateAuthority
private final DeviceRegister deviceRegister;
private final DeviceManager deviceManager;
private final DBManager<TPM2ProvisionerState> tpm2ProvisionerStateDBManager;
private final ReferenceDigestManager referenceDigestManager;
private String tpmQuoteHash = "";
private String tpmQuoteSignature = "";
private String pcrValues;
@ -185,6 +188,7 @@ public abstract class AbstractAttestationCertificateAuthority
* @param validDays the number of days issued certs are valid
* @param deviceManager the device manager
* @param tpm2ProvisionerStateDBManager the DBManager for persisting provisioner state
* @param referenceDigestManager the reference digest manager
*/
@SuppressWarnings("checkstyle:parameternumber")
public AbstractAttestationCertificateAuthority(
@ -195,7 +199,8 @@ public abstract class AbstractAttestationCertificateAuthority
final ReferenceManifestManager referenceManifestManager,
final DeviceRegister deviceRegister, final int validDays,
final DeviceManager deviceManager,
final DBManager<TPM2ProvisionerState> tpm2ProvisionerStateDBManager) {
final DBManager<TPM2ProvisionerState> tpm2ProvisionerStateDBManager,
final ReferenceDigestManager referenceDigestManager) {
this.supplyChainValidationService = supplyChainValidationService;
this.privateKey = privateKey;
this.acaCertificate = acaCertificate;
@ -206,6 +211,7 @@ public abstract class AbstractAttestationCertificateAuthority
this.validDays = validDays;
this.deviceManager = deviceManager;
this.tpm2ProvisionerStateDBManager = tpm2ProvisionerStateDBManager;
this.referenceDigestManager = referenceDigestManager;
}
/**
@ -841,6 +847,15 @@ public abstract class AbstractAttestationCertificateAuthority
support.resetCreateTime();
this.referenceManifestManager.update(support);
}
// this is where we update or create the log
ReferenceDigestRecord rdr = new ReferenceDigestRecord(support,
hw.getManufacturer(), hw.getProductName());
referenceDigestManager.saveRecord(rdr);
} catch (IOException ioEx) {
LOG.error(ioEx);
}

View File

@ -1,5 +1,7 @@
package hirs.attestationca.configuration;
import hirs.persist.DBReferenceDigestManager;
import hirs.persist.ReferenceDigestManager;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
@ -252,6 +254,16 @@ public class AttestationCertificateAuthorityConfiguration extends WebMvcConfigur
return new DBReferenceManifestManager(sessionFactory.getObject());
}
/**
* Creates a {@link ReferenceDigestManager} ready to use.
*
* @return {@link ReferenceDigestManager}
*/
@Bean
public ReferenceDigestManager referenceDigestManager() {
return new DBReferenceDigestManager(sessionFactory.getObject());
}
@Override
public void addResourceHandlers(final ResourceHandlerRegistry resourceHandlerRegistry) {
resourceHandlerRegistry.addResourceHandler("/client-files/**")

View File

@ -1,6 +1,7 @@
package hirs.attestationca.rest;
import hirs.persist.DBManager;
import hirs.persist.ReferenceDigestManager;
import hirs.persist.TPM2ProvisionerState;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
@ -42,6 +43,7 @@ public class RestfulAttestationCertificateAuthority
* @param validDays the number of days issued certs are valid
* @param deviceManager the device manager
* @param tpm2ProvisionerStateDBManager the DBManager for persisting provisioner state
* @param referenceDigestManager the reference digest manager
*/
@SuppressWarnings({ "checkstyle:parameternumber" })
@Autowired
@ -54,11 +56,12 @@ public class RestfulAttestationCertificateAuthority
final DeviceRegister deviceRegister,
final DeviceManager deviceManager,
final DBManager<TPM2ProvisionerState> tpm2ProvisionerStateDBManager,
final ReferenceDigestManager referenceDigestManager,
@Value("${aca.certificates.validity}") final int validDays) {
super(supplyChainValidationService, privateKey, acaCertificate, structConverter,
certificateManager, referenceManifestManager,
deviceRegister, validDays, deviceManager,
tpm2ProvisionerStateDBManager);
tpm2ProvisionerStateDBManager, referenceDigestManager);
}
/*

View File

@ -133,7 +133,7 @@ public class AbstractAttestationCertificateAuthorityTest {
public void setup() {
aca = new AbstractAttestationCertificateAuthority(null, keyPair.getPrivate(),
null, null, null, null, null, 1,
null, null) {
null, null, null) {
};
}

View File

@ -5,6 +5,7 @@ import hirs.persist.AppraiserManager;
import hirs.persist.CrudManager;
import hirs.persist.DBAppraiserManager;
import hirs.persist.DBCertificateManager;
import hirs.persist.DBReferenceDigestManager;
import hirs.persist.DBReferenceManifestManager;
import hirs.persist.DBDeviceGroupManager;
import hirs.persist.DBDeviceManager;
@ -71,6 +72,16 @@ public class PersistenceConfiguration {
return new DBReferenceManifestManager(sessionFactory.getObject());
}
/**
* Creates a {@link DBReferenceDigestManager} ready to use.
*
* @return {@link DBReferenceDigestManager}
*/
@Bean
public DBReferenceDigestManager referenceDigestManager() {
return new DBReferenceDigestManager(sessionFactory.getObject());
}
/**
* Creates a {@link AppraiserManager} ready to use.
*

View File

@ -1,10 +1,11 @@
package hirs.data.persist;
import org.bouncycastle.util.Arrays;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Table;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
/**
* This class will represent an entry a table that'll be associated
@ -15,38 +16,89 @@ import java.util.List;
@Table(name = "ReferenceDigestRecord")
public class ReferenceDigestRecord extends ArchivableEntity {
@Column
private UUID supportRim;
@Column(nullable = false)
private String manufacturer;
@Column(nullable = false)
private String model;
@Column(columnDefinition = "blob", nullable = false)
@Column(columnDefinition = "blob", nullable = true)
private byte[] valueBlob;
// NOTE: when this works, and do a show tables to give to Lawrence
private List<ReferenceDigestValue> associatedDigests = new ArrayList<>();
@Column
private boolean supportLoaded;
/**
* Default Constructor.
*/
protected ReferenceDigestRecord() {
super();
this.manufacturer = null;
this.model = null;
// I wonder if this will throw and error
this.supportRim = UUID.randomUUID();
this.manufacturer = "";
this.model = "";
this.valueBlob = null;
this.supportLoaded = false;
}
/**
* Default constructor with parameters.
* @param supportRim link to the source of data
* @param manufacturer device manufacturer
* @param model device model
* @param valueBlob the data values of the event.
*/
public ReferenceDigestRecord(final String manufacturer,
public ReferenceDigestRecord(final UUID supportRim,
final String manufacturer,
final String model,
final byte[] valueBlob) {
super();
// need to put up nullable entries
this.supportRim = supportRim;
this.manufacturer = manufacturer;
this.model = model;
this.valueBlob = valueBlob.clone();
this.valueBlob = Arrays.clone(valueBlob);
}
/**
* Default constructor with parameters specitic to a RIM object.
* @param referenceManifest rim object to use.
* @param manufacturer device manufacturer
* @param model device model
*/
public ReferenceDigestRecord(final ReferenceManifest referenceManifest,
final String manufacturer,
final String model) {
super();
if (referenceManifest instanceof SupportReferenceManifest) {
this.supportRim = referenceManifest.getId();
this.supportLoaded = true;
SupportReferenceManifest srm = (SupportReferenceManifest) referenceManifest;
this.valueBlob = Arrays.clone(srm.getRimBytes());
} else if (referenceManifest != null) {
// the assumption is that there is a support RIM.
this.supportRim = referenceManifest.getAssociatedRim();
// I will just test for loaded and if true but blob is empty, pull
// that information later and update the object
}
this.manufacturer = manufacturer;
this.model = model;
}
/**
* Getter for the linked source for the data.
* @return UUID of the source
*/
public UUID getSupportRim() {
return supportRim;
}
/**
* Setter for the linked source for the data.
* @param supportRim UUID of the source
*/
public void setSupportRim(final UUID supportRim) {
this.supportRim = supportRim;
}
/**
@ -98,4 +150,14 @@ public class ReferenceDigestRecord extends ArchivableEntity {
this.valueBlob = valueBlob.clone();
}
}
/**
* The string value representative of this class.
* @return manufacturer and model for this record
*/
@Override
public String toString() {
return String.format("%s%n%s -> %s",
super.toString(), this.manufacturer, this.model);
}
}

View File

@ -23,6 +23,10 @@ public class ReferenceDigestValue {
}
/**
* Maybe add the match fail status to the device object: eventNumber, digest value
*/
/**
* Default Constructor with a parameter for the data.
* @param data event data
@ -31,6 +35,7 @@ public class ReferenceDigestValue {
this.chunk = data.clone();
int i = 0;
this.eventNumber = data[i];
// look to using the Digest class
this.digestValue = String.valueOf(data[++i]);
this.eventType = String.valueOf(data[++i]);
this.tagId = String.valueOf(data[++i]);

View File

@ -4,10 +4,8 @@ import hirs.data.persist.ReferenceDigestRecord;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.hibernate.SessionFactory;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* This class is used to persist and retrieve {@link hirs.data.persist.ReferenceDigestRecord}s into
@ -27,27 +25,51 @@ public class DBReferenceDigestManager extends DBManager<ReferenceDigestRecord>
super(ReferenceDigestRecord.class, sessionFactory);
}
/**
* 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 referenceDigestSelector a configured
* {@link ReferenceDigestSelector} to use for querying
* @return the resulting set of ReferenceManifest, possibly empty
*/
@Override
@SuppressWarnings("unchecked")
public <T extends ReferenceDigestRecord> Set<T> get(
final ReferenceDigestSelector referenceDigestSelector) {
LOGGER.info("Getting the full set of Reference Digest Records.");
return new HashSet<>(
(List<T>) getWithCriteria(
referenceDigestSelector.getReferenceDigestClass(),
Collections.singleton(referenceDigestSelector.getCriterion())
)
);
public ReferenceDigestRecord saveRecord(final ReferenceDigestRecord referenceDigestRecord) {
LOGGER.debug("saving state: {}", referenceDigestRecord);
try {
return save(referenceDigestRecord);
} catch (DBManagerException dbMEx) {
throw new RuntimeException(dbMEx);
}
}
@Override
public ReferenceDigestRecord getRecord(final ReferenceDigestRecord referenceDigestRecord) {
LOGGER.debug("Getting record for {}", referenceDigestRecord);
if (referenceDigestRecord == null) {
LOGGER.error("null referenceDigestRecord argument");
throw new NullPointerException("null referenceDigestRecord");
}
return null;
}
@Override
public List<ReferenceDigestRecord> getRecordsByManufacturer(
final ReferenceDigestRecord referenceDigestRecord) {
LOGGER.debug("Getting records by manufacturer for {}", referenceDigestRecord);
if (referenceDigestRecord == null) {
LOGGER.error("null referenceDigestRecord argument");
throw new NullPointerException("null referenceDigestRecord");
}
return null;
}
@Override
public List<ReferenceDigestRecord> getRecordsByModel(
final ReferenceDigestRecord referenceDigestRecord) {
LOGGER.debug("Getting records by model for {}", referenceDigestRecord);
if (referenceDigestRecord == null) {
LOGGER.error("null referenceDigestRecord argument");
throw new NullPointerException("null referenceDigestRecord");
}
return null;
}
@Override
public boolean updateRecord(final ReferenceDigestRecord referenceDigestRecord) {
return false;
}
/**
@ -56,7 +78,8 @@ public class DBReferenceDigestManager extends DBManager<ReferenceDigestRecord>
* @param referenceDigestRecord the referenceDigestRecord to delete
* @return true if deletion was successful, false otherwise
*/
public boolean deleteReferenceManifest(final ReferenceDigestRecord referenceDigestRecord) {
@Override
public boolean deleteRecord(final ReferenceDigestRecord referenceDigestRecord) {
LOGGER.info(String.format("Deleting reference to %s/%s",
referenceDigestRecord.getManufacturer(), referenceDigestRecord.getModel()));
return delete(referenceDigestRecord);

View File

@ -154,6 +154,19 @@ public class PersistenceConfiguration {
return manager;
}
/**
* Creates a {@link ReferenceDigestManager} ready to use.
*
* @return {@link ReferenceDigestManager}
*/
@Bean
public ReferenceDigestManager referenceDigestManager() {
DBReferenceDigestManager manager
= new DBReferenceDigestManager(sessionFactory.getObject());
setDbManagerRetrySettings(manager);
return manager;
}
/**
* Creates a {@link DeviceStateManager} ready to use.
*

View File

@ -3,13 +3,13 @@ package hirs.persist;
import hirs.data.persist.ReferenceDigestRecord;
import java.util.Set;
import java.util.List;
/**
* This class facilitates the persistence of {@link hirs.data.persist.ReferenceDigestRecord}s
* including storage, retrieval, and deletion.
*/
public interface ReferenceDigestManager extends OrderedListQuerier<ReferenceDigestRecord> {
public interface ReferenceDigestManager {
/**
* Persists a new Reference Digest.
@ -17,28 +17,45 @@ public interface ReferenceDigestManager extends OrderedListQuerier<ReferenceDige
* @param referenceDigestRecord the ReferenceDigestRecord
* @return the persisted ReferenceDigestRecord
*/
ReferenceDigestRecord save(ReferenceDigestRecord referenceDigestRecord);
ReferenceDigestRecord saveRecord(ReferenceDigestRecord referenceDigestRecord);
/**
* Persists a new Reference Digest.
*
* @param referenceDigestRecord the ReferenceDigestRecord
* @return the persisted ReferenceDigestRecord
*/
ReferenceDigestRecord getRecord(ReferenceDigestRecord referenceDigestRecord);
/**
* Persists a new Reference Digest.
*
* @param referenceDigestRecord the ReferenceDigestRecord
* @return the persisted ReferenceDigestRecord
*/
List<ReferenceDigestRecord> getRecordsByManufacturer(
ReferenceDigestRecord referenceDigestRecord);
/**
* Persists a new Reference Digest.
*
* @param referenceDigestRecord the ReferenceDigestRecord
* @return the persisted ReferenceDigestRecord
*/
List<ReferenceDigestRecord> getRecordsByModel(ReferenceDigestRecord referenceDigestRecord);
/**
* Updates an existing ReferenceDigestRecord.
* @param referenceDigestRecord the Reference Digest update
* @return status of successful update
*/
void update(ReferenceDigestRecord referenceDigestRecord);
boolean updateRecord(ReferenceDigestRecord referenceDigestRecord);
/**
* Retrieve Reference Digest according to the given {@link ReferenceDigestSelector}.
* Delete the given record.
*
* @param <T> the type of reference digest that will be retrieved
* @param referenceDigestSelector a {@link ReferenceDigestSelector} to use for querying
* @return a Set of matching RIMs, which may be empty
*/
<T extends ReferenceDigestRecord> Set<T> get(ReferenceDigestSelector referenceDigestSelector);
/**
* Delete the given RIM.
*
* @param referenceDigestRecord the RIM to delete
* @param referenceDigestRecord the digest record delete
* @return true if the deletion succeeded, false otherwise.
*/
boolean delete(ReferenceDigestRecord referenceDigestRecord);
boolean deleteRecord(ReferenceDigestRecord referenceDigestRecord);
}

View File

@ -1,209 +0,0 @@
package hirs.persist;
import com.google.common.base.Preconditions;
import hirs.data.persist.ReferenceDigestRecord;
import hirs.data.persist.certificate.Certificate;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.hibernate.criterion.Conjunction;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Restrictions;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* This class is used to select one or many RIMs in conjunction
* with a {@link ReferenceDigestManager}. 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 ReferenceDigestSelector<T extends ReferenceDigestRecord> {
/**
* String representing the database field for the manufacturer.
*/
public static final String PLATFORM_MANUFACTURER = "manufacturer";
/**
* String representing the database field for the manufacturer id.
*/
public static final String PLATFORM_MODEL = "model";
private final ReferenceDigestManager referenceDigestManager;
private final Class<T> referenceTypeClass;
private final Map<String, Object> fieldValueSelections;
private boolean excludeArchivedRims;
/**
* Default Constructor.
*
* @param referenceDigestManager the RIM manager to be used to retrieve RIMs
* @param referenceTypeClass the type of Reference Manifest to process.
*/
public ReferenceDigestSelector(final ReferenceDigestManager referenceDigestManager,
final Class<T> referenceTypeClass) {
this(referenceDigestManager, referenceTypeClass, true);
}
/**
* Standard Constructor for the Selector.
*
* @param referenceDigestManager 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 ReferenceDigestSelector(final ReferenceDigestManager referenceDigestManager,
final Class<T> referenceTypeClass,
final boolean excludeArchivedRims) {
Preconditions.checkArgument(
referenceDigestManager != null,
"reference manifest manager cannot be null"
);
Preconditions.checkArgument(
referenceTypeClass != null,
"type cannot be null"
);
this.referenceDigestManager = referenceDigestManager;
this.referenceTypeClass = referenceTypeClass;
this.excludeArchivedRims = excludeArchivedRims;
this.fieldValueSelections = new HashMap<>();
}
/**
* Specify the entity id that rims must have to be considered as matching.
*
* @param manufacturer the UUID to query
* @return this instance (for chaining further calls)
*/
public ReferenceDigestSelector<T> byManufacturer(final String manufacturer) {
setFieldValue(PLATFORM_MANUFACTURER, manufacturer);
return this;
}
/**
* Specify the hash code of the bytes that rim must match.
*
* @param model the hash code of the bytes to query for
* @return this instance (for chaining further calls)
*/
public ReferenceDigestSelector<T> byModel(final String model) {
setFieldValue(PLATFORM_MODEL, model);
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 hirs.data.persist.ReferenceDigestRecord}. 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 digest record or null if none is found
*/
public T getDigestRecord() {
Set<T> rims = execute();
if (rims.isEmpty()) {
return null;
}
return rims.iterator().next();
}
/**
* Retrieve the result set as a set of
* {@link hirs.data.persist.ReferenceDigestRecord}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> getDigestRecords() {
return Collections.unmodifiableSet(new HashSet<>(execute()));
}
/**
* Construct the criterion that can be used to query for rims matching the
* configuration of this {@link hirs.persist.ReferenceDigestSelector}.
*
* @return a Criterion that can be used to query for rims matching the
* configuration of this instance
*/
Criterion getCriterion() {
Conjunction conj = new Conjunction();
for (Map.Entry<String, Object> fieldValueEntry : fieldValueSelections.entrySet()) {
conj.add(Restrictions.eq(fieldValueEntry.getKey(), fieldValueEntry.getValue()));
}
if (this.excludeArchivedRims) {
conj.add(Restrictions.isNull(Certificate.ARCHIVE_FIELD));
}
return conj;
}
/**
* @return the rim class that this instance will query
*/
public Class<T> getReferenceDigestClass() {
return this.referenceTypeClass;
}
// construct and execute query
private Set<T> execute() {
Set<T> results = this.referenceDigestManager.get(this);
return results;
}
/**
* Configures the selector to query for archived and unarchived rims.
*
* @return the selector
*/
public ReferenceDigestSelector<T> includeArchived() {
this.excludeArchivedRims = false;
return this;
}
}