fixed merge conflict

This commit is contained in:
iadgovuser26 2024-03-14 15:57:56 -04:00
commit 8e1ec11c41
88 changed files with 5027 additions and 896 deletions

View File

@ -3,6 +3,9 @@ LABEL org.opencontainers.image.vendor NSA Laboratory for Advanced Cybersecurity
LABEL org.opencontainers.image.source https://github.com/nsacyber/hirs
LABEL org.opencontainers.image.description NSA\'s HIRS Attestation Certificate Authority. Expose port 8443 to access the portal from outside the container.
# REF can be specified as a docker run environment variable to select the HIRS branch to work with
ARG REF=main
SHELL ["/bin/bash", "-c"]
# Rocky 9 has a different channel for some apps
@ -50,7 +53,7 @@ RUN echo "#!/bin/bash" > /tmp/tpm_config && \
EXPOSE 8443
# Checkout HIRS
RUN git clone -b main https://github.com/nsacyber/HIRS.git /repo
RUN git clone -b ${REF} https://github.com/nsacyber/HIRS.git /repo
# Defensive copy of the repo so it's easy to start fresh if needed
RUN mkdir /hirs

View File

@ -9,6 +9,9 @@ LABEL org.opencontainers.image.source https://github.com/nsacyber/hirs
LABEL org.opencontainers.image.description NSA\'s HIRS Attestation Certificate Authority in a Windows-native image. Expose port 8443 to access the portal from outside the container.
LABEL org.opencontainers.image.base.name mcr.microsoft.com/powershell:${BASE_IMAGE_TAG}
# REF can be specified as a docker run environment variable to select the HIRS branch to work with
ARG REF=main
SHELL ["pwsh", "-Command"]
# Output Powershell Version
@ -105,11 +108,11 @@ RUN setx PATH '%JAVA_HOME%\bin;C:\Program Files\MariaDB 11.1\bin;%GIT_HOME%\bin;
# Echo PATH after update
RUN echo $Env:PATH
# Clone HIRS main
# Clone HIRS main (or REF)
WORKDIR C:/
RUN git config --global --add core.autocrlf false
RUN git config --global --add safe.directory '*'
RUN git clone -b main https://github.com/nsacyber/hirs.git C:/repo
RUN git clone -b ${REF} https://github.com/nsacyber/hirs.git C:/repo
# Defensive copy of the repo so it's easy to start fresh if needed
WORKDIR C:/repo

View File

@ -51,6 +51,7 @@ jobs:
echo "PUBLIC_IMAGE_TAG=$PUBLIC_IMAGE_NAME:$IMAGE_TAG_VAR" >> "$GITHUB_OUTPUT"
- name: Print env
run: |
echo GITHUB_REF_NAME=${{ github.ref_name }}
echo DOCKERFILE_ROCKY=$DOCKERFILE_ROCKY
echo DOCKERFILE_WINDOWS=$DOCKERFILE_WINDOWS
echo IMAGE_NAME_ROCKY=$IMAGE_NAME_ROCKY
@ -86,6 +87,7 @@ jobs:
with:
context: "{{defaultContext}}:.ci/docker"
file: Dockerfile.${{env.DOCKERFILE_ROCKY}}
build-args: REF=${{ github.ref_name }}
tags: ${{env.TAG}}
push: true
@ -108,7 +110,7 @@ jobs:
- name: Build the docker image for ${{ github.repository }}
run: |
cd ./.ci/docker
docker build -f ./Dockerfile.${{env.DOCKERFILE_WINDOWS}} -t ${{env.TAG}} .
docker build --build-arg "REF=${{ github.ref_name }}" -f ./Dockerfile.${{env.DOCKERFILE_WINDOWS}} -t ${{env.TAG}} .
- name: Push the docker image
run: |
@ -133,7 +135,7 @@ jobs:
- name: Build the docker image for ${{ github.repository }}
run: |
cd ./.ci/docker
docker build -f ./Dockerfile.${{env.DOCKERFILE_WINDOWS}} -t ${{env.TAG}} --build-arg BASE_IMAGE_TAG=lts-windowsservercore-1809 .
docker build --build-arg "REF=${{ github.ref_name }}" -f ./Dockerfile.${{env.DOCKERFILE_WINDOWS}} -t ${{env.TAG}} --build-arg BASE_IMAGE_TAG=lts-windowsservercore-1809 .
- name: Push the docker image
run: |

View File

@ -52,6 +52,7 @@ dependencies {
testImplementation 'org.junit.jupiter:junit-jupiter:5.9.3'
testImplementation 'org.junit.platform:junit-platform-launcher:1.9.3'
testImplementation 'org.mockito:mockito-core:4.2.0'
testImplementation 'org.springframework:spring-test:6.0.8'
// spring management
compileOnly libs.lombok

View File

@ -15,5 +15,9 @@
<Match>
<Bug pattern="EI_EXPOSE_REP2" />
</Match>
<Match>
<Class name="hirs.attestationca.persist.AttestationCertificateAuthorityTest"/>
<Bug pattern="NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE"/>
</Match>
</FindBugsFilter>

View File

@ -1,6 +1,7 @@
package hirs.attestationca.persist;
import hirs.attestationca.persist.entity.manager.CertificateRepository;
import hirs.attestationca.persist.entity.manager.ComponentInfoRepository;
import hirs.attestationca.persist.entity.manager.ComponentResultRepository;
import hirs.attestationca.persist.entity.manager.DeviceRepository;
import hirs.attestationca.persist.entity.manager.IssuedCertificateRepository;
@ -52,6 +53,7 @@ public abstract class AttestationCertificateAuthority {
private Integer validDays = 1;
private final ComponentResultRepository componentResultRepository;
private ComponentInfoRepository componentInfoRepository;
private final CertificateRepository certificateRepository;
private final IssuedCertificateRepository issuedCertificateRepository;
private final ReferenceManifestRepository referenceManifestRepository;
@ -71,13 +73,14 @@ public abstract class AttestationCertificateAuthority {
* @param acaCertificate the ACA certificate
* @param structConverter the struct converter
* @param componentResultRepository the component result manager
* @param componentInfoRepository the component info manager
* @param certificateRepository the certificate manager
* @param referenceManifestRepository the Reference Manifest manager
* @param validDays the number of days issued certs are valid
* @param deviceRepository the device manager
* @param referenceDigestValueRepository the reference event manager
* @param policyRepository
* @param tpm2ProvisionerStateRepository
* @param policyRepository policy setting repository
* @param tpm2ProvisionerStateRepository tpm2 provisioner state repository
*/
@SuppressWarnings("checkstyle:parameternumber")
public AttestationCertificateAuthority(
@ -85,6 +88,7 @@ public abstract class AttestationCertificateAuthority {
final PrivateKey privateKey, final X509Certificate acaCertificate,
final StructConverter structConverter,
final ComponentResultRepository componentResultRepository,
final ComponentInfoRepository componentInfoRepository,
final CertificateRepository certificateRepository,
final IssuedCertificateRepository issuedCertificateRepository,
final ReferenceManifestRepository referenceManifestRepository,
@ -98,6 +102,7 @@ public abstract class AttestationCertificateAuthority {
this.acaCertificate = acaCertificate;
this.structConverter = structConverter;
this.componentResultRepository = componentResultRepository;
this.componentInfoRepository = componentInfoRepository;
this.certificateRepository = certificateRepository;
this.issuedCertificateRepository = issuedCertificateRepository;
this.referenceManifestRepository = referenceManifestRepository;
@ -111,8 +116,8 @@ public abstract class AttestationCertificateAuthority {
certificateRepository, deviceRepository,
privateKey, acaCertificate, validDays, tpm2ProvisionerStateRepository);
this.identityClaimHandler = new IdentityClaimProcessor(supplyChainValidationService,
certificateRepository, referenceManifestRepository,
referenceDigestValueRepository,
certificateRepository, componentResultRepository, componentInfoRepository,
referenceManifestRepository, referenceDigestValueRepository,
deviceRepository, tpm2ProvisionerStateRepository, policyRepository);
}

View File

@ -1,6 +1,7 @@
package hirs.attestationca.persist;
import hirs.attestationca.persist.entity.manager.CertificateRepository;
import hirs.attestationca.persist.entity.manager.ComponentInfoRepository;
import hirs.attestationca.persist.entity.manager.ComponentResultRepository;
import hirs.attestationca.persist.entity.manager.DeviceRepository;
import hirs.attestationca.persist.entity.manager.IssuedCertificateRepository;
@ -10,7 +11,6 @@ import hirs.attestationca.persist.entity.manager.ReferenceManifestRepository;
import hirs.attestationca.persist.entity.manager.TPM2ProvisionerStateRepository;
import hirs.attestationca.persist.service.SupplyChainValidationService;
import hirs.structs.converters.StructConverter;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
@ -63,6 +63,7 @@ public class RestfulAttestationCertificateAuthority extends AttestationCertifica
final PrivateKey privateKey, final X509Certificate acaCertificate,
final StructConverter structConverter,
final ComponentResultRepository componentResultRepository,
final ComponentInfoRepository componentInfoRepository,
final CertificateRepository certificateRepository,
final IssuedCertificateRepository issuedCertificateRepository,
final ReferenceManifestRepository referenceManifestRepository,
@ -72,7 +73,8 @@ public class RestfulAttestationCertificateAuthority extends AttestationCertifica
final PolicyRepository policyRepository,
final TPM2ProvisionerStateRepository tpm2ProvisionerStateRepository) {
super(supplyChainValidationService, privateKey, acaCertificate, structConverter,
componentResultRepository, certificateRepository, issuedCertificateRepository,
componentResultRepository, componentInfoRepository,
certificateRepository, issuedCertificateRepository,
referenceManifestRepository,
validDays, deviceRepository,
referenceDigestValueRepository, policyRepository, tpm2ProvisionerStateRepository);

View File

@ -0,0 +1,32 @@
package hirs.attestationca.persist.entity.manager;
import hirs.attestationca.persist.entity.userdefined.certificate.attributes.ComponentAttributeResult;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
import java.util.UUID;
public interface ComponentAttributeRepository extends JpaRepository<ComponentAttributeResult, UUID> {
/**
* Query to look up Attribute Results based on the PlatformCredential's
* db component id.
* @param componentId the unique id for the component identifier
* @return a list of attribute results
*/
List<ComponentAttributeResult> findByComponentId(UUID componentId);
/**
* Query to look up Attribute Results based on the validation id.
* @param provisionSessionId unique id generated to link supply chain summary
* @return a list of attribute results
*/
List<ComponentAttributeResult> findByProvisionSessionId(UUID provisionSessionId);
/**
* Query to look up Attribute Results based on the component id and the session id.
* @param componentId the unique id for the component identifier
* @param provisionSessionId unique id generated to link supply chain summary
* @return a list of attribute results
*/
List<ComponentAttributeResult> findByComponentIdAndProvisionSessionId(UUID componentId, UUID provisionSessionId);
}

View File

@ -0,0 +1,25 @@
package hirs.attestationca.persist.entity.manager;
import hirs.attestationca.persist.entity.userdefined.info.ComponentInfo;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
import java.util.UUID;
public interface ComponentInfoRepository extends JpaRepository<ComponentInfo, UUID> {
/**
* Query that retrieves device components by device name.
* @param deviceName string for the host name
* @return a list of device components
*/
List<ComponentInfo> findByDeviceName(String deviceName);
/**
* Query that retrieves device components by device name and
* the component serial number.
* @param deviceName string for the host name
* @param componentSerial string for the component serial
* @return a list of device components
*/
List<ComponentInfo> findByDeviceNameAndComponentSerial(String deviceName, String componentSerial);
}

View File

@ -2,7 +2,6 @@ package hirs.attestationca.persist.entity.manager;
import hirs.attestationca.persist.entity.userdefined.certificate.ComponentResult;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
import java.util.List;
@ -11,6 +10,19 @@ import java.util.UUID;
@Repository
public interface ComponentResultRepository extends JpaRepository<ComponentResult, UUID> {
@Query(value = "SELECT * FROM ComponentResult where certificateId = ?1", nativeQuery = true)
List<ComponentResult> getComponentResultsByCertificate(UUID certificateId);
/**
* Query based on the device serial number.
* @param boardSerialNumber variable holding the device serial number
* @return a list of component result.
*/
List<ComponentResult> findByBoardSerialNumber(String boardSerialNumber);
/**
* Query based on certificate serial number and device serial number.
* @param certificateSerialNumber certificate specific serial number
* @param boardSerialNumber variable holding the device serial number
* @return a list of component result.
*/
List<ComponentResult> findByCertificateSerialNumberAndBoardSerialNumber(
String certificateSerialNumber, String boardSerialNumber);
}

View File

@ -2,7 +2,6 @@ package hirs.attestationca.persist.entity.manager;
import hirs.attestationca.persist.entity.userdefined.rim.ReferenceDigestValue;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
import java.util.List;

View File

@ -90,7 +90,8 @@ public class TPM2ProvisionerState {
/**
* Convenience method for finding the {@link TPM2ProvisionerState} associated with the nonce.
*
* @param tpm2ProvisionerStateRepository the {@link TPM2ProvisionerStateRepository} to use when looking for the
* @param tpm2ProvisionerStateRepository the {@link TPM2ProvisionerStateRepository}
* to use when looking for the
* {@link TPM2ProvisionerState}
* @param nonce the nonce to use as the key for the {@link TPM2ProvisionerState}
* @return the {@link TPM2ProvisionerState} associated with the nonce;

View File

@ -115,8 +115,7 @@ public class Device extends AbstractEntity {
public String toString() {
return String.format("Device Name: %s%nStatus: %s%nSummary: %s%n",
name, healthStatus.getStatus(),
supplyChainValidationStatus.toString(),
summaryId);
supplyChainValidationStatus.toString());
}
@Override

View File

@ -46,6 +46,7 @@ public class SupplyChainValidationSummary extends ArchivableEntity {
private static final String DEVICE_ID_FIELD = "device.id";
@Getter
@Column
@Enumerated(EnumType.STRING)
private final AppraisalStatus.Status overallValidationResult;
@ -58,6 +59,9 @@ public class SupplyChainValidationSummary extends ArchivableEntity {
targetEntity = SupplyChainValidation.class, orphanRemoval = true)
private final Set<SupplyChainValidation> validations;
@Column
private UUID provisionSessionId;
/**
* Default constructor necessary for Hibernate.
*/
@ -177,6 +181,20 @@ public class SupplyChainValidationSummary extends ArchivableEntity {
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
* @param provisionSessionId randomly generated UUID to associate with results
*/
public SupplyChainValidationSummary(final Device device,
final Collection<SupplyChainValidation> validations,
final UUID provisionSessionId) {
this(device, validations);
this.provisionSessionId = provisionSessionId;
}
/**
* Construct a new SupplyChainValidationSummary.
*
@ -212,13 +230,6 @@ public class SupplyChainValidationSummary extends ArchivableEntity {
return new Device(this.device.getDeviceInfo());
}
/**
* @return the overall appraisal result
*/
public AppraisalStatus.Status getOverallValidationResult() {
return overallValidationResult;
}
/**
* @return the validations that this summary contains
*/

View File

@ -1,38 +1,142 @@
package hirs.attestationca.persist.entity.userdefined.certificate;
import hirs.attestationca.persist.entity.AbstractEntity;
import hirs.attestationca.persist.entity.ArchivableEntity;
import hirs.attestationca.persist.entity.userdefined.certificate.attributes.ComponentAddress;
import hirs.attestationca.persist.entity.userdefined.certificate.attributes.ComponentIdentifier;
import hirs.attestationca.persist.entity.userdefined.certificate.attributes.V2.AttributeStatus;
import hirs.attestationca.persist.entity.userdefined.certificate.attributes.V2.ComponentIdentifierV2;
import jakarta.persistence.Entity;
import lombok.AccessLevel;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
@EqualsAndHashCode(callSuper=false)
/**
* A component result is a DO to hold the status of a component validation status. This will
* also be used to display this common information on the certificate details page.
*/
@Getter
@Entity
@EqualsAndHashCode(callSuper = false)
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class ComponentResult extends AbstractEntity {
public class ComponentResult extends ArchivableEntity {
private UUID certificateId;
private int componentHash;
private String expected;
private String actual;
private boolean mismatched;
// embedded component info
@Setter
private String manufacturer;
@Setter
private String model;
private String serialNumber;
private String revisionNumber;
private boolean fieldReplaceable = false;
// this is a string because component class doesn't inherit serializable.
@Setter
private String componentClassValue;
private String componentClassStr;
private String componentClassType;
private AttributeStatus attributeStatus;
private String componentAddress;
private boolean version2 = false;
@Setter
private boolean failedValidation;
private String certificateType;
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);
private String issuerDN;
private String certificateSerialNumber;
private String boardSerialNumber;
private String uniformResourceIdentifier;
/**
* Default constructor.
* @param boardSerialNumber associated platform certificate serial number.
* @param certificateSerialNumber unique number associated with header info.
* @param certificateType parameter holds version 1.2 or 2.0.
* @param componentIdentifier object with information from the platform certificate components.
*/
public ComponentResult(final String boardSerialNumber, final String certificateSerialNumber,
final String certificateType,
final ComponentIdentifier componentIdentifier) {
this.boardSerialNumber = boardSerialNumber;
this.certificateSerialNumber = certificateSerialNumber;
this.certificateType = certificateType;
this.manufacturer = componentIdentifier.getComponentManufacturer().toString();
this.model = componentIdentifier.getComponentModel().toString();
this.serialNumber = componentIdentifier.getComponentSerial().toString();
this.revisionNumber = componentIdentifier.getComponentRevision().toString();
if (componentIdentifier.getFieldReplaceable() != null) {
this.fieldReplaceable = componentIdentifier.getFieldReplaceable().isTrue();
}
StringBuilder sb = new StringBuilder();
for (ComponentAddress element : componentIdentifier.getComponentAddress()) {
sb.append(String.format("%s:%s;", element.getAddressTypeValue(),
element.getAddressValue().toString()));
}
componentAddress = sb.toString();
// V2 fields
if (componentIdentifier.isVersion2()
&& componentIdentifier instanceof ComponentIdentifierV2) {
// this is a downside of findbugs, the code is set up to indicate if a CI is V2 or not
// but find bugs is throwing a flag because instanceof isn't being used.
ComponentIdentifierV2 ciV2 = (ComponentIdentifierV2) componentIdentifier;
this.componentClassValue = ciV2.getComponentClass().getComponentIdentifier();
this.componentClassStr = ciV2.getComponentClass().toString();
this.componentClassType = ciV2.getComponentClass().getRegistryType();
this.attributeStatus = ciV2.getAttributeStatus();
this.version2 = true;
if (ciV2.getCertificateIdentifier() != null) {
this.issuerDN = ciV2.getCertificateIdentifier().getIssuerDN().toString();
if (ciV2.getComponentPlatformUri() != null) {
this.uniformResourceIdentifier = ciV2.getComponentPlatformUri()
.getUniformResourceIdentifier().toString();
}
}
}
}
/**
* This method is only used by the certificate-details.jsp page. This
* method splits the compiled string of addresses into the component address
* object for display on the jsp page.
* @return a collection of component addresses.
*/
public List<ComponentAddress> getComponentAddresses() {
List<ComponentAddress> addresses = new LinkedList<>();
ComponentAddress address;
if (componentAddress != null && !componentAddress.isEmpty()) {
for (String s : componentAddress.split(";", 0)) {
address = new ComponentAddress();
address.setAddressTypeString(s.split(":")[0]);
address.setAddressValueString(s.split(":")[1]);
addresses.add(address);
}
}
return addresses;
}
/**
* Returns a hash code that is associated with common fields for components.
* @return int value of the elements
*/
public int hashCommonElements() {
return Objects.hash(manufacturer,
model, serialNumber, revisionNumber, componentClassValue);
}
/**
* The string method for log entries.
* @return a string for the component result
*/
public String toString() {
return String.format("ComponentResult[%d]: expected=[%s] actual=[%s]",
componentHash, expected, actual);
return String.format("ComponentResult: certificateSerialNumber=[%s] "
+ "manufacturer=[%s] model=[%s] componentClass=[%s]",
boardSerialNumber, manufacturer, model, componentClassValue);
}
}

View File

@ -7,7 +7,6 @@ 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.URIReference;
import hirs.attestationca.persist.entity.userdefined.certificate.attributes.V2.PlatformConfigurationV2;
import hirs.attestationca.persist.service.selector.CertificateSelector;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Transient;
@ -483,7 +482,7 @@ public class PlatformCredential extends DeviceAssociatedCertificate {
Map<String, Object> attributes = new HashMap<>();
ASN1Sequence attributeSequence;
// Check all attributes for Platform Configuration
for (ASN1Encodable enc: getAttributeCertificate().getAcinfo().getAttributes().toArray()) {
for (ASN1Encodable enc : getAttributeCertificate().getAcinfo().getAttributes().toArray()) {
Attribute attr = Attribute.getInstance(enc);
attributeSequence
= ASN1Sequence.getInstance(attr.getAttrValues().getObjectAt(0));

View File

@ -32,6 +32,8 @@ public class ComponentAddress {
private ASN1ObjectIdentifier addressType;
private ASN1UTF8String addressValue;
private String addressTypeString;
private String addressValueString;
/**
* Default constructor.

View File

@ -0,0 +1,49 @@
package hirs.attestationca.persist.entity.userdefined.certificate.attributes;
import hirs.attestationca.persist.entity.ArchivableEntity;
import jakarta.persistence.Entity;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import java.util.UUID;
/**
* This is tied to the ComponentResult class. If a component has a mismatched
* value from what the device has listed, this class represents which attribute
* of that component mismatched.
*/
@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class ComponentAttributeResult extends ArchivableEntity {
private UUID componentId;
@Setter
private UUID provisionSessionId;
private String expectedValue;
private String actualValue;
/**
* Default constructor that populates the expected and actual values.
* @param componentId id associated with component result
* @param expectedValue platform certificate value
* @param actualValue paccor value from the device
*/
public ComponentAttributeResult(final UUID componentId,
final String expectedValue,
final String actualValue) {
this.componentId = componentId;
this.expectedValue = expectedValue;
this.actualValue = actualValue;
}
/**
* This method is used to check the mismatched status flag for
* displaying red if there is a failure.
*/
public boolean checkMatchedStatus() {
return this.actualValue.equals(this.expectedValue);
}
}

View File

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

View File

@ -1,6 +1,5 @@
package hirs.attestationca.persist.entity.userdefined.certificate.attributes;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
@ -37,10 +36,6 @@ import java.util.stream.Collectors;
@EqualsAndHashCode
public class ComponentIdentifier {
/**
* Variable for components that aren't set.
*/
public static final String EMPTY_COMPONENT = "[Empty]";
/**
* Variable for components that aren't set.
*/
@ -88,8 +83,8 @@ public class ComponentIdentifier {
public ComponentIdentifier() {
componentManufacturer = new DERUTF8String(NOT_SPECIFIED_COMPONENT);
componentModel = new DERUTF8String(NOT_SPECIFIED_COMPONENT);
componentSerial = new DERUTF8String(EMPTY_COMPONENT);
componentRevision = new DERUTF8String(EMPTY_COMPONENT);
componentSerial = new DERUTF8String(NOT_SPECIFIED_COMPONENT);
componentRevision = new DERUTF8String(NOT_SPECIFIED_COMPONENT);
componentManufacturerId = null;
fieldReplaceable = null;
componentAddress = new ArrayList<>();
@ -124,13 +119,13 @@ public class ComponentIdentifier {
/**
* Constructor given the SEQUENCE that contains Component Identifier.
* @param sequence containing the the component identifier
* @param sequence containing the component identifier
* @throws IllegalArgumentException if there was an error on the parsing
*/
public ComponentIdentifier(final ASN1Sequence sequence) throws IllegalArgumentException {
// set all optional values to default in case they aren't set.
this();
//Check if it have a valid number of identifiers
//Check if it has a valid number of identifiers
if (sequence.size() < MANDATORY_ELEMENTS) {
throw new IllegalArgumentException("Component identifier do not have required values.");
}
@ -238,7 +233,7 @@ public class ComponentIdentifier {
sb.append(fieldReplaceable.toString());
}
sb.append(", componentAddress=");
if (componentAddress.size() > 0) {
if (!componentAddress.isEmpty()) {
sb.append(componentAddress
.stream()
.map(Object::toString)

View File

@ -25,7 +25,7 @@ public class PlatformConfigurationV1 extends PlatformConfiguration {
/**
* Constructor given the SEQUENCE that contains Platform Configuration.
* @param sequence containing the the Platform Configuration.
* @param sequence containing the Platform Configuration.
* @throws IllegalArgumentException if there was an error on the parsing
*/
public PlatformConfigurationV1(final ASN1Sequence sequence) throws IllegalArgumentException {

View File

@ -103,13 +103,13 @@ public class ComponentIdentifierV2 extends ComponentIdentifier {
/**
* Constructor given the SEQUENCE that contains Component Identifier.
* @param sequence containing the the component identifier
* @param sequence containing the component identifier
* @throws IllegalArgumentException if there was an error on the parsing
*/
public ComponentIdentifierV2(final ASN1Sequence sequence)
throws IllegalArgumentException {
super();
// Check if it have a valid number of identifiers
// Check if it has a valid number of identifiers
if (sequence.size() < MANDATORY_ELEMENTS) {
throw new IllegalArgumentException("Component identifier do not have required values.");
}

View File

@ -1,36 +1,39 @@
package hirs.attestationca.persist.entity.userdefined.info;
import hirs.attestationca.persist.entity.ArchivableEntity;
import hirs.attestationca.persist.entity.userdefined.certificate.attributes.ComponentIdentifier;
import hirs.utils.enums.DeviceInfoEnums;
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.Data;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
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.
*/
@Log4j2
@NoArgsConstructor
@Data
@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@DiscriminatorColumn(name = "componentTypeEnum", discriminatorType = DiscriminatorType.STRING)
public class ComponentInfo implements Serializable {
public class ComponentInfo extends ArchivableEntity {
@Id
@Column(name = "componentInfo_id")
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
// @Id
// @Column(name = "componentInfo_id")
// @GeneratedValue(strategy = GenerationType.AUTO)
// private Long id;
@Column(nullable = false)
private String deviceName;
@XmlElement
@Column(nullable = false)
private String componentManufacturer;
@ -52,7 +55,7 @@ public class ComponentInfo implements Serializable {
private String componentClass;
/**
* Constructor.
* Base constructor for children.
* @param componentManufacturer Component Manufacturer (must not be null)
* @param componentModel Component Model (must not be null)
* @param componentSerial Component Serial Number (can be null)
@ -62,6 +65,22 @@ public class ComponentInfo implements Serializable {
final String componentModel,
final String componentSerial,
final String componentRevision) {
this(DeviceInfoEnums.NOT_SPECIFIED, componentManufacturer, componentModel,
componentSerial, componentRevision);
}
/**
* Constructor.
* @param deviceName the host machine associated with this component. (must not be null)
* @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 deviceName,
final String componentManufacturer,
final String componentModel,
final String componentSerial,
final String componentRevision) {
if (isComplete(
componentManufacturer,
componentModel,
@ -72,61 +91,40 @@ public class ComponentInfo implements Serializable {
throw new NullPointerException("ComponentInfo: manufacturer and/or "
+ "model can not be null");
}
this.deviceName = deviceName;
this.componentManufacturer = componentManufacturer.trim();
this.componentModel = componentModel.trim();
if (componentSerial != null) {
this.componentSerial = componentSerial.trim();
} else {
this.componentSerial = StringUtils.EMPTY;
this.componentSerial = ComponentIdentifier.NOT_SPECIFIED_COMPONENT;
}
if (componentRevision != null) {
this.componentRevision = componentRevision.trim();
} else {
this.componentRevision = StringUtils.EMPTY;
this.componentRevision = ComponentIdentifier.NOT_SPECIFIED_COMPONENT;
}
}
/**
* Constructor.
* @param deviceName the host machine associated with this component.
* @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,
public ComponentInfo(final String deviceName,
final String componentManufacturer,
final String componentModel,
final String componentSerial,
final String componentRevision,
final String componentClass) {
if (isComplete(
componentManufacturer,
componentModel,
componentSerial,
componentRevision)) {
log.error("ComponentInfo: manufacturer and/or "
+ "model can not be null");
throw new NullPointerException("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;
}
this(deviceName, componentManufacturer, componentModel,
componentSerial, componentRevision);
if (componentClass != null) {
this.componentClass = componentClass;
} else {
this.componentClass = StringUtils.EMPTY;
}
this.componentClass = Objects.requireNonNullElse(componentClass, StringUtils.EMPTY);
}
/**
@ -148,4 +146,44 @@ public class ComponentInfo implements Serializable {
return (StringUtils.isEmpty(componentManufacturer)
|| StringUtils.isEmpty(componentModel));
}
/**
* Equals for the component info that just uses this classes attributes.
* @param object the object to compare
* @return the boolean result
*/
@Override
public boolean equals(Object object) {
if (this == object) return true;
if (object == null || getClass() != object.getClass()) return false;
ComponentInfo that = (ComponentInfo) object;
return Objects.equals(deviceName, that.deviceName)
&& 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);
}
/**
* Returns a hash code that is associated with common fields for components.
* @return int value of the elements
*/
public int hashCommonElements() {
return Objects.hash(componentManufacturer, componentModel,
componentSerial, componentRevision, componentClass);
}
/**
* Hash method for the attributes of this class.
* @return int value that represents this class
*/
@Override
public int hashCode() {
return Objects.hash(deviceName, componentManufacturer,
componentModel, componentSerial, componentRevision,
componentClass);
}
}

View File

@ -19,26 +19,22 @@ import java.net.UnknownHostException;
* Store information about the Portal into the database.
*/
@NoArgsConstructor
@Getter
@Entity
@Table(name = "PortalInfo")
@Access(AccessType.FIELD)
public class PortalInfo {
@Id
@Getter
@Column
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@Getter
@Column(unique = true, nullable = false)
private String name;
@Getter
@Column
private InetAddress ipAddress;
@Getter
@Column
private int port = 0;
@Getter
@Column
private String context;
@ -57,14 +53,14 @@ public class PortalInfo {
/**
* Stores the address of the portal.
*
* @param newip address used by the portal.
* @param inetAddress address used by the portal.
*/
public void setIpAddress(final InetAddress newip) {
if (newip == null) {
public void setIpAddress(final InetAddress inetAddress) {
if (inetAddress == null) {
throw new IllegalArgumentException("setIpAddress input was null.");
}
ipAddress = newip;
this.ipAddress = inetAddress;
}
/**
@ -74,18 +70,18 @@ public class PortalInfo {
* @throws UnknownHostException For problems resolving or storing the host.
*/
public void setIpAddress(final String host) throws UnknownHostException {
ipAddress = InetAddress.getByName(host);
this.ipAddress = InetAddress.getByName(host);
}
/**
* Store the port of the portal.
*
* @param newport port of the portal
* @param port port of the portal
*/
public void setPort(final int newport) {
public void setPort(final int port) {
final int upperBound = 65535;
if (newport > 0 && newport <= upperBound) {
port = newport;
if (port > 0 && port <= upperBound) {
this.port = port;
} else {
throw new IllegalArgumentException("Failed to store portal port. Provided number was"
+ " outside of valid range (1 - " + upperBound + ")");

View File

@ -28,8 +28,8 @@ import java.util.Objects;
* OS, and TPM information.
*/
@Log4j2
@Getter
@NoArgsConstructor
@Getter
@Entity
public class DeviceInfoReport extends AbstractEntity implements Serializable {
@ -196,7 +196,7 @@ public class DeviceInfoReport extends AbstractEntity implements Serializable {
return hardwareInfo;
}
private void setNetworkInfo(NetworkInfo networkInfo) {
private void setNetworkInfo(final NetworkInfo networkInfo) {
if (networkInfo == null) {
log.error("NetworkInfo cannot be null");
throw new NullPointerException("network info");
@ -204,7 +204,7 @@ public class DeviceInfoReport extends AbstractEntity implements Serializable {
this.networkInfo = networkInfo;
}
private void setOSInfo(OSInfo osInfo) {
private void setOSInfo(final OSInfo osInfo) {
if (osInfo == null) {
log.error("OSInfo cannot be null");
throw new NullPointerException("os info");
@ -212,7 +212,7 @@ public class DeviceInfoReport extends AbstractEntity implements Serializable {
this.osInfo = osInfo;
}
private void setFirmwareInfo(FirmwareInfo firmwareInfo) {
private void setFirmwareInfo(final FirmwareInfo firmwareInfo) {
if (firmwareInfo == null) {
log.error("FirmwareInfo cannot be null");
throw new NullPointerException("firmware info");
@ -220,7 +220,7 @@ public class DeviceInfoReport extends AbstractEntity implements Serializable {
this.firmwareInfo = firmwareInfo;
}
private void setHardwareInfo(HardwareInfo hardwareInfo) {
private void setHardwareInfo(final HardwareInfo hardwareInfo) {
if (hardwareInfo == null) {
log.error("HardwareInfo cannot be null");
throw new NullPointerException("hardware info");
@ -228,7 +228,7 @@ public class DeviceInfoReport extends AbstractEntity implements Serializable {
this.hardwareInfo = hardwareInfo;
}
private void setTPMInfo(TPMInfo tpmInfo) {
private void setTPMInfo(final TPMInfo tpmInfo) {
this.tpmInfo = tpmInfo;
}

View File

@ -51,6 +51,11 @@ public class AbstractProcessor {
@Getter
private PolicyRepository policyRepository;
/**
* Default constructor that sets main class fields.
* @param privateKey private key used for communication authentication
* @param validDays property value to set for issued certificates
*/
public AbstractProcessor(final PrivateKey privateKey,
final int validDays) {
this.privateKey = privateKey;
@ -64,6 +69,7 @@ public class AbstractProcessor {
* @param endorsementCredential the endorsement credential
* @param platformCredentials the set of platform credentials
* @param deviceName The host name used in the subject alternative name
* @param acaCertificate object used to create credential
* @return identity credential
*/
protected X509Certificate generateCredential(final PublicKey publicKey,
@ -159,7 +165,7 @@ public class AbstractProcessor {
* @param endorsementCredential an endorsement credential to check if platform credentials
* exist
* @param certificateRepository db connector from certificates
* @return the Set of Platform Credentials, if they exist, an empty set otherwise
* @return the List of Platform Credentials, if they exist, an empty set otherwise
*/
protected List<PlatformCredential> parsePcsFromIdentityClaim(
final ProvisionerTpm2.IdentityClaim identityClaim,
@ -187,10 +193,12 @@ public class AbstractProcessor {
/**
* Gets the Endorsement Credential from the DB given the EK public key.
* @param ekPublicKey the EK public key
* @param certificateRepository db store manager for certificates
* @return the Endorsement credential, if found, otherwise null
*/
private EndorsementCredential getEndorsementCredential(final PublicKey ekPublicKey,
final CertificateRepository certificateRepository) {
private EndorsementCredential getEndorsementCredential(
final PublicKey ekPublicKey,
final CertificateRepository certificateRepository) {
log.debug("Searching for endorsement credential based on public key: " + ekPublicKey);
if (ekPublicKey == null) {
@ -220,6 +228,7 @@ public class AbstractProcessor {
* Helper method to create an {@link IssuedAttestationCertificate} object, set its
* corresponding device and persist it.
*
* @param certificateRepository db store manager for certificates
* @param derEncodedAttestationCertificate the byte array representing the Attestation
* certificate
* @param endorsementCredential the endorsement credential used to generate the AC

View File

@ -37,8 +37,11 @@ public class CertificateRequestProcessor extends AbstractProcessor {
/**
* Constructor.
* @param supplyChainValidationService object that is used to run provisioning
* @param certificateRepository db connector for all certificates.
* @param deviceRepository database connector for Devices.
* @param privateKey private key used for communication authentication
* @param acaCertificate object used to create credential
* @param validDays int for the time in which a certificate is valid.
* @param tpm2ProvisionerStateRepository db connector for provisioner state.
*/
@ -97,7 +100,8 @@ public class CertificateRequestProcessor extends AbstractProcessor {
RSAPublicKey akPub = ProvisionUtils.parsePublicKey(claim.getAkPublicArea().toByteArray());
// Get Endorsement Credential if it exists or was uploaded
EndorsementCredential endorsementCredential = parseEcFromIdentityClaim(claim, ekPub, certificateRepository);
EndorsementCredential endorsementCredential = parseEcFromIdentityClaim(claim,
ekPub, certificateRepository);
// Get Platform Credentials if they exist or were uploaded
List<PlatformCredential> platformCredentials = parsePcsFromIdentityClaim(claim,

View File

@ -3,18 +3,24 @@ package hirs.attestationca.persist.provision;
import com.google.protobuf.ByteString;
import hirs.attestationca.configuration.provisionerTpm2.ProvisionerTpm2;
import hirs.attestationca.persist.entity.manager.CertificateRepository;
import hirs.attestationca.persist.entity.manager.ComponentInfoRepository;
import hirs.attestationca.persist.entity.manager.ComponentResultRepository;
import hirs.attestationca.persist.entity.manager.DeviceRepository;
import hirs.attestationca.persist.entity.manager.PolicyRepository;
import hirs.attestationca.persist.entity.manager.ReferenceDigestValueRepository;
import hirs.attestationca.persist.entity.manager.ReferenceManifestRepository;
import hirs.attestationca.persist.entity.manager.TPM2ProvisionerStateRepository;
import hirs.attestationca.persist.entity.tpm.TPM2ProvisionerState;
import hirs.attestationca.persist.entity.userdefined.Certificate;
import hirs.attestationca.persist.entity.userdefined.Device;
import hirs.attestationca.persist.entity.userdefined.PolicySettings;
import hirs.attestationca.persist.entity.userdefined.ReferenceManifest;
import hirs.attestationca.persist.entity.userdefined.SupplyChainValidationSummary;
import hirs.attestationca.persist.entity.userdefined.certificate.ComponentResult;
import hirs.attestationca.persist.entity.userdefined.certificate.EndorsementCredential;
import hirs.attestationca.persist.entity.userdefined.certificate.PlatformCredential;
import hirs.attestationca.persist.entity.userdefined.certificate.attributes.ComponentIdentifier;
import hirs.attestationca.persist.entity.userdefined.info.ComponentInfo;
import hirs.attestationca.persist.entity.userdefined.info.FirmwareInfo;
import hirs.attestationca.persist.entity.userdefined.info.HardwareInfo;
import hirs.attestationca.persist.entity.userdefined.info.NetworkInfo;
@ -29,6 +35,7 @@ import hirs.attestationca.persist.enums.AppraisalStatus;
import hirs.attestationca.persist.exceptions.IdentityProcessingException;
import hirs.attestationca.persist.provision.helper.ProvisionUtils;
import hirs.attestationca.persist.service.SupplyChainValidationService;
import hirs.attestationca.persist.validation.SupplyChainCredentialValidator;
import hirs.utils.HexUtils;
import hirs.utils.SwidResource;
import hirs.utils.enums.DeviceInfoEnums;
@ -70,17 +77,21 @@ public class IdentityClaimProcessor extends AbstractProcessor {
private SupplyChainValidationService supplyChainValidationService;
private CertificateRepository certificateRepository;
private ComponentResultRepository componentResultRepository;
private ComponentInfoRepository componentInfoRepository;
private ReferenceManifestRepository referenceManifestRepository;
private ReferenceDigestValueRepository referenceDigestValueRepository;
private DeviceRepository deviceRepository;
private TPM2ProvisionerStateRepository tpm2ProvisionerStateRepository;
/**
* Constructor
* Constructor.
*/
public IdentityClaimProcessor(
final SupplyChainValidationService supplyChainValidationService,
final CertificateRepository certificateRepository,
final ComponentResultRepository componentResultRepository,
final ComponentInfoRepository componentInfoRepository,
final ReferenceManifestRepository referenceManifestRepository,
final ReferenceDigestValueRepository referenceDigestValueRepository,
final DeviceRepository deviceRepository,
@ -88,6 +99,8 @@ public class IdentityClaimProcessor extends AbstractProcessor {
final PolicyRepository policyRepository) {
this.supplyChainValidationService = supplyChainValidationService;
this.certificateRepository = certificateRepository;
this.componentResultRepository = componentResultRepository;
this.componentInfoRepository = componentInfoRepository;
this.referenceManifestRepository = referenceManifestRepository;
this.referenceDigestValueRepository = referenceDigestValueRepository;
this.deviceRepository = deviceRepository;
@ -187,7 +200,9 @@ public class IdentityClaimProcessor extends AbstractProcessor {
// Parse and save device info
Device device = processDeviceInfo(claim);
device.getDeviceInfo().setPaccorOutputString(claim.getPaccorOutput());
// device.getDeviceInfo().setPaccorOutputString(claim.getPaccorOutput());
handleDeviceComponents(device.getDeviceInfo().getNetworkInfo().getHostname(),
claim.getPaccorOutput());
// There are situations in which the claim is sent with no PCs
// or a PC from the tpm which will be deprecated
// this is to check what is in the platform object and pull
@ -203,9 +218,21 @@ public class IdentityClaimProcessor extends AbstractProcessor {
platformCredentials.addAll(tempList);
}
// store component results objects
for (PlatformCredential platformCredential : platformCredentials) {
List<ComponentResult> componentResults = componentResultRepository
.findByCertificateSerialNumberAndBoardSerialNumber(
platformCredential.getSerialNumber().toString(),
platformCredential.getPlatformSerial());
if (componentResults.isEmpty()) {
handlePlatformComponents(platformCredential);
}
}
// perform supply chain validation
SupplyChainValidationSummary summary = supplyChainValidationService.validateSupplyChain(
endorsementCredential, platformCredentials, device);
endorsementCredential, platformCredentials, device,
componentInfoRepository.findByDeviceName(device.getName()));
device.setSummaryId(summary.getId().toString());
// update the validation result in the device
AppraisalStatus.Status validationResult = summary.getOverallValidationResult();
@ -231,7 +258,12 @@ public class IdentityClaimProcessor extends AbstractProcessor {
log.info("Processing Device Info Report");
// store device and device info report.
Device device = this.deviceRepository.findByName(deviceInfoReport.getNetworkInfo().getHostname());
Device device = null;
if (deviceInfoReport.getNetworkInfo() != null
&& deviceInfoReport.getNetworkInfo().getHostname() != null
&& !deviceInfoReport.getNetworkInfo().getHostname().isEmpty()) {
device = this.deviceRepository.findByName(deviceInfoReport.getNetworkInfo().getHostname());
}
if (device == null) {
device = new Device(deviceInfoReport);
}
@ -302,7 +334,7 @@ public class IdentityClaimProcessor extends AbstractProcessor {
pcrValues = dv.getPcrslist().toStringUtf8();
}
// check for RIM Base and Support files, if they don't exists in the database, load them
// check for RIM Base and Support files, if they don't exist in the database, load them
String defaultClientName = String.format("%s_%s",
dv.getHw().getManufacturer(),
dv.getHw().getProductName());
@ -314,7 +346,6 @@ public class IdentityClaimProcessor extends AbstractProcessor {
Pattern pattern = Pattern.compile("([^\\s]+(\\.(?i)(rimpcr|rimel|bin|log))$)");
Matcher matcher;
MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
// List<ReferenceManifest> listOfSavedRims = new LinkedList<>();
if (dv.getLogfileCount() > 0) {
for (ByteString logFile : dv.getLogfileList()) {
@ -424,11 +455,9 @@ public class IdentityClaimProcessor extends AbstractProcessor {
dbSupport.setUpdated(true);
dbSupport.setAssociatedRim(dbBaseRim.getId());
this.referenceManifestRepository.save(dbSupport);
// listOfSavedRims.add(dbSupport);
}
}
this.referenceManifestRepository.save(dbBaseRim);
// listOfSavedRims.add(dbBaseRim);
}
}
@ -586,44 +615,66 @@ public class IdentityClaimProcessor extends AbstractProcessor {
log.error(String.format("Patching value does not exist (%s)",
patchedValue));
} else {
/**
* Until we get patch examples, this is WIP
*/
// WIP - Until we get patch examples
dbRdv.setPatched(true);
}
}
}
} catch (CertificateException cEx) {
log.error(cEx);
} catch (NoSuchAlgorithmException noSaEx) {
log.error(noSaEx);
} catch (IOException ioEx) {
log.error(ioEx);
} catch (CertificateException | NoSuchAlgorithmException | IOException ex) {
log.error(ex);
}
}
return true;
}
private int handlePlatformComponents(final Certificate certificate) {
PlatformCredential platformCredential;
int componentResults = 0;
if (certificate instanceof PlatformCredential) {
platformCredential = (PlatformCredential) certificate;
ComponentResult componentResult;
for (ComponentIdentifier componentIdentifier : platformCredential
.getComponentIdentifiers()) {
private List<PlatformCredential> getPlatformCredentials(final CertificateRepository certificateRepository,
final EndorsementCredential ec) {
List<PlatformCredential> credentials = null;
if (ec == null) {
log.warn("Cannot look for platform credential(s). Endorsement credential was null.");
} else {
log.debug("Searching for platform credential(s) based on holder serial number: "
+ ec.getSerialNumber());
credentials = certificateRepository.getByHolderSerialNumber(ec.getSerialNumber());
if (credentials == null || credentials.isEmpty()) {
log.warn("No platform credential(s) found");
} else {
log.debug("Platform Credential(s) found: " + credentials.size());
componentResult = new ComponentResult(platformCredential.getPlatformSerial(),
platformCredential.getSerialNumber().toString(),
platformCredential.getPlatformChainType(),
componentIdentifier);
componentResult.setFailedValidation(false);
componentResultRepository.save(componentResult);
componentResults++;
}
}
return componentResults;
}
return credentials;
private int handleDeviceComponents(final String hostName, final String paccorString) {
int deviceComponents = 0 ;
Map<Integer, ComponentInfo> componentInfoMap = new HashMap<>();
try {
List<ComponentInfo> componentInfos = SupplyChainCredentialValidator
.getComponentInfoFromPaccorOutput(hostName, paccorString);
// check the DB for like component infos
List<ComponentInfo> dbComponentInfos = this.componentInfoRepository.findByDeviceName(hostName);
dbComponentInfos.stream().forEach((infos) -> {
componentInfoMap.put(infos.hashCode(), infos);
});
for (ComponentInfo componentInfo : dbComponentInfos) {
if (componentInfoMap.containsKey(componentInfo.hashCode())) {
componentInfos.remove(componentInfo);
}
}
for (ComponentInfo componentInfo : componentInfos) {
this.componentInfoRepository.save(componentInfo);
}
} catch (IOException ioEx) {
log.warn("Error parsing paccor string");
}
return deviceComponents;
}
}

View File

@ -5,11 +5,8 @@ import hirs.attestationca.persist.entity.manager.CertificateRepository;
import hirs.attestationca.persist.entity.userdefined.certificate.EndorsementCredential;
import hirs.attestationca.persist.entity.userdefined.certificate.PlatformCredential;
import lombok.extern.log4j.Log4j2;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.List;
import java.util.stream.Collectors;
/**

View File

@ -4,8 +4,6 @@ import hirs.attestationca.persist.entity.userdefined.certificate.EndorsementCred
import hirs.attestationca.persist.entity.userdefined.certificate.PlatformCredential;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.ASN1OctetString;
import org.bouncycastle.asn1.DEROctetString;
@ -42,13 +40,13 @@ public final class IssuedCertificateAttributeHelper {
/**
* Object Identifier TCPA at TPM ID Label.
*/
public final static ASN1ObjectIdentifier TCPA_AT_TPM_ID_LABEL =
public static final ASN1ObjectIdentifier TCPA_AT_TPM_ID_LABEL =
new ASN1ObjectIdentifier(TPM_ID_LABEL_OID);
/**
* The extended key usage extension.
*/
public final static Extension EXTENDED_KEY_USAGE_EXTENSION;
private final static ASN1ObjectIdentifier TCG_KP_AIK_CERTIFICATE_ATTRIBUTE =
public static final Extension EXTENDED_KEY_USAGE_EXTENSION;
private static final ASN1ObjectIdentifier TCG_KP_AIK_CERTIFICATE_ATTRIBUTE =
new ASN1ObjectIdentifier("2.23.133.8.3");
static {

View File

@ -4,6 +4,7 @@ import hirs.attestationca.persist.DBManagerException;
import hirs.attestationca.persist.entity.ArchivableEntity;
import hirs.attestationca.persist.entity.manager.CACredentialRepository;
import hirs.attestationca.persist.entity.manager.CertificateRepository;
import hirs.attestationca.persist.entity.manager.ComponentAttributeRepository;
import hirs.attestationca.persist.entity.manager.ComponentResultRepository;
import hirs.attestationca.persist.entity.manager.PolicyRepository;
import hirs.attestationca.persist.entity.manager.ReferenceDigestValueRepository;
@ -14,14 +15,18 @@ import hirs.attestationca.persist.entity.userdefined.Device;
import hirs.attestationca.persist.entity.userdefined.PolicySettings;
import hirs.attestationca.persist.entity.userdefined.SupplyChainValidation;
import hirs.attestationca.persist.entity.userdefined.SupplyChainValidationSummary;
import hirs.attestationca.persist.entity.userdefined.certificate.ComponentResult;
import hirs.attestationca.persist.entity.userdefined.certificate.EndorsementCredential;
import hirs.attestationca.persist.entity.userdefined.certificate.PlatformCredential;
import hirs.attestationca.persist.entity.userdefined.certificate.attributes.ComponentAttributeResult;
import hirs.attestationca.persist.entity.userdefined.info.ComponentInfo;
import hirs.attestationca.persist.entity.userdefined.rim.EventLogMeasurements;
import hirs.attestationca.persist.entity.userdefined.rim.SupportReferenceManifest;
import hirs.attestationca.persist.enums.AppraisalStatus;
import hirs.attestationca.persist.validation.PcrValidator;
import hirs.attestationca.persist.validation.SupplyChainCredentialValidator;
import lombok.extern.log4j.Log4j2;
import org.apache.logging.log4j.Level;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@ -32,8 +37,7 @@ import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.apache.logging.log4j.Level;
import java.util.UUID;
import static hirs.attestationca.persist.enums.AppraisalStatus.Status.FAIL;
import static hirs.attestationca.persist.enums.AppraisalStatus.Status.PASS;
@ -47,9 +51,11 @@ public class SupplyChainValidationService {
private ReferenceManifestRepository referenceManifestRepository;
private ReferenceDigestValueRepository referenceDigestValueRepository;
private ComponentResultRepository componentResultRepository;
private ComponentAttributeRepository componentAttributeRepository;
private CertificateRepository certificateRepository;
private SupplyChainValidationRepository supplyChainValidationRepository;
private SupplyChainValidationSummaryRepository supplyChainValidationSummaryRepository;
private UUID provisionSessionId;
/**
* Constructor.
@ -70,6 +76,7 @@ public class SupplyChainValidationService {
final PolicyRepository policyRepository,
final CertificateRepository certificateRepository,
final ComponentResultRepository componentResultRepository,
final ComponentAttributeRepository componentAttributeRepository,
final ReferenceManifestRepository referenceManifestRepository,
final SupplyChainValidationRepository supplyChainValidationRepository,
final SupplyChainValidationSummaryRepository supplyChainValidationSummaryRepository,
@ -78,6 +85,7 @@ public class SupplyChainValidationService {
this.policyRepository = policyRepository;
this.certificateRepository = certificateRepository;
this.componentResultRepository = componentResultRepository;
this.componentAttributeRepository = componentAttributeRepository;
this.referenceManifestRepository = referenceManifestRepository;
this.supplyChainValidationRepository = supplyChainValidationRepository;
this.supplyChainValidationSummaryRepository = supplyChainValidationSummaryRepository;
@ -92,13 +100,16 @@ public class SupplyChainValidationService {
* @param ec The endorsement credential from the identity request.
* @param pcs The platform credentials from the identity request.
* @param device The device to be validated.
* @param componentInfos list of components from the device
* @return A summary of the validation results.
*/
@SuppressWarnings("methodlength")
public SupplyChainValidationSummary validateSupplyChain(final EndorsementCredential ec,
final List<PlatformCredential> pcs,
final Device device) {
final Device device,
final List<ComponentInfo> componentInfos) {
boolean acceptExpiredCerts = getPolicySettings().isExpiredCertificateValidationEnabled();
provisionSessionId = UUID.randomUUID();
PlatformCredential baseCredential = null;
SupplyChainValidation platformScv = null;
SupplyChainValidation basePlatformScv = null;
@ -205,6 +216,8 @@ public class SupplyChainValidationService {
null, Level.ERROR));
} else {
if (chkDeltas) {
// There are delta certificates, so the code need to build a new list of
// certificate components to then compare against the device component list
aes.addAll(basePlatformScv.getCertificatesUsed());
Iterator<PlatformCredential> it = pcs.iterator();
while (it.hasNext()) {
@ -220,16 +233,23 @@ public class SupplyChainValidationService {
}
}
} else {
// validate attributes for a single base platform certificate
aes.add(baseCredential);
validations.remove(platformScv);
// if there are no deltas, just check base credential
platformScv = ValidationService.evaluatePCAttributesStatus(
baseCredential, device.getDeviceInfo(), ec,
certificateRepository, componentResultRepository);
certificateRepository, componentResultRepository,
componentAttributeRepository, componentInfos, provisionSessionId);
validations.add(new SupplyChainValidation(
SupplyChainValidation.ValidationType.PLATFORM_CREDENTIAL,
platformScv.getValidationResult(), aes, platformScv.getMessage()));
}
updateComponentStatus(componentResultRepository
.findByCertificateSerialNumberAndBoardSerialNumber(
baseCredential.getSerialNumber().toString(),
baseCredential.getPlatformSerial()));
}
if (!attrErrorMessage.isEmpty()) {
//combine platform and platform attributes
@ -252,7 +272,7 @@ public class SupplyChainValidationService {
log.info("The validation finished, summarizing...");
// Generate validation summary, save it, and return it.
SupplyChainValidationSummary summary
= new SupplyChainValidationSummary(device, validations);
= new SupplyChainValidationSummary(device, validations, provisionSessionId);
try {
supplyChainValidationSummaryRepository.save(summary);
} catch (DBManagerException dbMEx) {
@ -374,4 +394,25 @@ public class SupplyChainValidationService {
}
return defaultSettings;
}
/**
* If the platform attributes policy is enabled, this method updates the matched
* status for the component result. This is done so that the details page for the
* platform certificate highlights the title card red.
* @param componentResults list of associated component results
*/
private void updateComponentStatus(final List<ComponentResult> componentResults) {
List<ComponentAttributeResult> componentAttributeResults = componentAttributeRepository
.findByProvisionSessionId(provisionSessionId);
List<UUID> componentIdList = new ArrayList<>();
for (ComponentAttributeResult componentAttributeResult : componentAttributeResults) {
componentIdList.add(componentAttributeResult.getComponentId());
}
for (ComponentResult componentResult : componentResults) {
componentResult.setFailedValidation(componentIdList.contains(componentResult.getId()));
componentResultRepository.save(componentResult);
}
}
}

View File

@ -3,6 +3,7 @@ package hirs.attestationca.persist.service;
import hirs.attestationca.persist.entity.ArchivableEntity;
import hirs.attestationca.persist.entity.manager.CACredentialRepository;
import hirs.attestationca.persist.entity.manager.CertificateRepository;
import hirs.attestationca.persist.entity.manager.ComponentAttributeRepository;
import hirs.attestationca.persist.entity.manager.ComponentResultRepository;
import hirs.attestationca.persist.entity.manager.ReferenceDigestValueRepository;
import hirs.attestationca.persist.entity.manager.ReferenceManifestRepository;
@ -12,9 +13,9 @@ import hirs.attestationca.persist.entity.userdefined.PolicySettings;
import hirs.attestationca.persist.entity.userdefined.ReferenceManifest;
import hirs.attestationca.persist.entity.userdefined.SupplyChainValidation;
import hirs.attestationca.persist.entity.userdefined.certificate.CertificateAuthorityCredential;
import hirs.attestationca.persist.entity.userdefined.certificate.ComponentResult;
import hirs.attestationca.persist.entity.userdefined.certificate.EndorsementCredential;
import hirs.attestationca.persist.entity.userdefined.certificate.PlatformCredential;
import hirs.attestationca.persist.entity.userdefined.info.ComponentInfo;
import hirs.attestationca.persist.entity.userdefined.report.DeviceInfoReport;
import hirs.attestationca.persist.enums.AppraisalStatus;
import hirs.attestationca.persist.validation.CertificateAttributeScvValidator;
@ -37,6 +38,7 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
@Log4j2
public class ValidationService {
@ -104,7 +106,10 @@ public class ValidationService {
final PlatformCredential pc, final DeviceInfoReport deviceInfoReport,
final EndorsementCredential ec,
final CertificateRepository certificateRepository,
final ComponentResultRepository componentResultRepository) {
final ComponentResultRepository componentResultRepository,
final ComponentAttributeRepository componentAttributeRepository,
final List<ComponentInfo> componentInfos,
final UUID provisionSessionId) {
final SupplyChainValidation.ValidationType validationType
= SupplyChainValidation.ValidationType.PLATFORM_CREDENTIAL_ATTRIBUTES;
@ -116,7 +121,9 @@ public class ValidationService {
}
log.info("Validating platform credential attributes");
AppraisalStatus result = CredentialValidator.
validatePlatformCredentialAttributes(pc, deviceInfoReport, ec);
validatePlatformCredentialAttributes(pc, deviceInfoReport, ec,
componentResultRepository, componentAttributeRepository,
componentInfos, provisionSessionId);
switch (result.getAppStatus()) {
case PASS:
return buildValidationRecord(validationType, AppraisalStatus.Status.PASS,
@ -126,10 +133,6 @@ public class ValidationService {
pc.setComponentFailures(result.getAdditionalInfo());
pc.setComponentFailureMessage(result.getMessage());
certificateRepository.save(pc);
for (ComponentResult componentResult
: CertificateAttributeScvValidator.getComponentResultList()) {
componentResultRepository.save(componentResult);
}
}
return buildValidationRecord(validationType, AppraisalStatus.Status.FAIL,
result.getMessage(), pc, Level.WARN);

View File

@ -4,11 +4,14 @@ import com.github.marandus.pciid.model.Device;
import com.github.marandus.pciid.model.Vendor;
import com.github.marandus.pciid.service.PciIdsDatabase;
import com.google.common.base.Strings;
import hirs.attestationca.persist.entity.userdefined.certificate.ComponentResult;
import hirs.attestationca.persist.entity.userdefined.certificate.attributes.ComponentClass;
import hirs.attestationca.persist.entity.userdefined.certificate.attributes.ComponentIdentifier;
import hirs.attestationca.persist.entity.userdefined.certificate.attributes.V2.ComponentIdentifierV2;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.bouncycastle.asn1.ASN1UTF8String;
import org.bouncycastle.asn1.DERUTF8String;
@ -23,6 +26,7 @@ import java.util.List;
/**
* Provide Java access to PCI IDs.
*/
@Log4j2
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class PciIds {
/**
@ -52,6 +56,7 @@ public final class PciIds {
String dbFile = null;
for (final String path : PCI_IDS_PATH) {
if ((new File(path)).exists()) {
log.info("PCI IDs file was found {}", path);
dbFile = path;
break;
}
@ -113,6 +118,23 @@ public final class PciIds {
return newList;
}
/**
* Iterate through all components and translate PCI hardware IDs as necessary. It will only
* translate ComponentResults objects as it relies on Component Class information.
* @param componentResults List of ComponentResults.
* @return the translated list of ComponentResults.
*/
public static List<ComponentResult> translateResults(final List<ComponentResult> componentResults) {
List<ComponentResult> newList = new ArrayList<>();
if (componentResults != null && !componentResults.isEmpty()) {
for (final ComponentResult componentResult : componentResults) {
newList.add(translateResult(componentResult));
}
}
return newList;
}
/**
* Translate Vendor and Device IDs, if found, in ComponentIdentifierV2 objects.
* It will only translate ID values, any other value will pass through.
@ -149,6 +171,23 @@ public final class PciIds {
return newComponent;
}
/**
* Translate Vendor and Device IDs, if found, in ComponentResult objects.
* It will only translate ID values, any other value will pass through.
* @param componentResult ComponentResult object.
* @return the translated ComponentResult object.
*/
public static ComponentResult translateResult(final ComponentResult componentResult) {
ComponentResult newComponent = null;
if (componentResult != null) {
newComponent = componentResult;
newComponent.setManufacturer(translateVendor(componentResult.getManufacturer()));
newComponent.setModel(translateDevice(componentResult.getManufacturer(),
componentResult.getModel()));
}
return newComponent;
}
/**
* Look up the vendor name from the PCI IDs list, if the input string contains an ID.
* If any part of this fails, return the original manufacturer value.
@ -166,6 +205,23 @@ public final class PciIds {
return manufacturer;
}
/**
* Look up the vendor name from the PCI IDs list, if the input string contains an ID.
* If any part of this fails, return the original manufacturer value.
* @param refManufacturer String, likely from a ComponentResult
* @return String with the discovered vendor name, or the original manufacturer value.
*/
public static String translateVendor(final String refManufacturer) {
String manufacturer = refManufacturer;
if (manufacturer != null && manufacturer.trim().matches("^[0-9A-Fa-f]{4}$")) {
Vendor ven = DB.findVendor(manufacturer.toLowerCase());
if (ven != null && !Strings.isNullOrEmpty(ven.getName())) {
manufacturer = ven.getName();
}
}
return manufacturer;
}
/**
* Look up the device name from the PCI IDs list, if the input strings contain IDs.
* The Device lookup requires the Vendor ID AND the Device ID to be valid values.
@ -190,4 +246,28 @@ public final class PciIds {
}
return model;
}
/**
* Look up the device name from the PCI IDs list, if the input strings contain IDs.
* The Device lookup requires the Vendor ID AND the Device ID to be valid values.
* If any part of this fails, return the original model value.
* @param refManufacturer String, likely from a ComponentResult
* @param refModel String, likely from a ComponentResult
* @return String with the discovered device name, or the original model value.
*/
public static String translateDevice(final String refManufacturer,
final String refModel) {
String model = refModel;
if (refManufacturer != null
&& model != null
&& refManufacturer.trim().matches("^[0-9A-Fa-f]{4}$")
&& model.trim().matches("^[0-9A-Fa-f]{4}$")) {
Device dev = DB.findDevice(refManufacturer.toLowerCase(),
model.toLowerCase());
if (dev != null && !Strings.isNullOrEmpty(dev.getName())) {
model = dev.getName();
}
}
return model;
}
}

View File

@ -1,9 +1,12 @@
package hirs.attestationca.persist.validation;
import hirs.attestationca.persist.entity.ArchivableEntity;
import hirs.attestationca.persist.entity.manager.ComponentAttributeRepository;
import hirs.attestationca.persist.entity.manager.ComponentResultRepository;
import hirs.attestationca.persist.entity.userdefined.SupplyChainValidation;
import hirs.attestationca.persist.entity.userdefined.certificate.ComponentResult;
import hirs.attestationca.persist.entity.userdefined.certificate.PlatformCredential;
import hirs.attestationca.persist.entity.userdefined.certificate.attributes.ComponentAttributeResult;
import hirs.attestationca.persist.entity.userdefined.certificate.attributes.ComponentIdentifier;
import hirs.attestationca.persist.entity.userdefined.certificate.attributes.V2.ComponentIdentifierV2;
import hirs.attestationca.persist.entity.userdefined.info.ComponentInfo;
@ -43,15 +46,7 @@ import static hirs.attestationca.persist.enums.AppraisalStatus.Status.PASS;
@Log4j2
public class CertificateAttributeScvValidator extends SupplyChainCredentialValidator {
private static List<ComponentResult> componentResultList = new LinkedList<>();
/**
* Getter for the list of components to verify.
* @return a collection of components
*/
public static List<ComponentResult> getComponentResultList() {
return Collections.unmodifiableList(componentResultList);
}
private static final String LC_UNKNOWN = "unknown";
/**
* Checks if the delta credential's attributes are valid.
@ -97,7 +92,7 @@ public class CertificateAttributeScvValidator extends SupplyChainCredentialValid
List<ComponentIdentifier> origPcComponents
= new LinkedList<>(basePlatformCredential.getComponentIdentifiers());
return validateDeltaAttributesChainV2p0(deltaPlatformCredential.getId(),
return validateDeltaAttributesChainV2p0(
deviceInfoReport, deltaMapping, origPcComponents);
}
@ -190,7 +185,6 @@ public class CertificateAttributeScvValidator extends SupplyChainCredentialValid
+ " did not match the Certificate's Serial Number";
log.error(message);
status = new AppraisalStatus(FAIL, message);
}
}
}
@ -198,16 +192,23 @@ public class CertificateAttributeScvValidator extends SupplyChainCredentialValid
return status;
}
/**
* Validates device info report against the new platform credential.
* @param platformCredential the Platform Credential
* @param deviceInfoReport the Device Info Report
* @param componentResultRepository db access to component result of mismatching
* @param componentAttributeRepository db access to component attribute match status
* @param componentInfos list of device components
* @param provisionSessionId UUID associated with the SCV Summary
* @return either PASS or FAIL
*/
public static AppraisalStatus validatePlatformCredentialAttributesV2p0(
final PlatformCredential platformCredential,
final DeviceInfoReport deviceInfoReport) {
final DeviceInfoReport deviceInfoReport,
final ComponentResultRepository componentResultRepository,
final ComponentAttributeRepository componentAttributeRepository,
final List<ComponentInfo> componentInfos,
final UUID provisionSessionId) {
boolean passesValidation = true;
StringBuilder resultMessage = new StringBuilder();
@ -236,17 +237,24 @@ public class CertificateAttributeScvValidator extends SupplyChainCredentialValid
passesValidation &= fieldValidation;
fieldValidation = requiredPlatformCredentialFieldIsNonEmptyAndMatches(
"PlatformVersion",
platformCredential.getVersion(),
hardwareInfo.getVersion());
if (!isNotSpecifiedOrUnknown(platformCredential.getVersion())
&& !isNotSpecifiedOrUnknown(hardwareInfo.getVersion())) {
fieldValidation = requiredPlatformCredentialFieldIsNonEmptyAndMatches(
"PlatformVersion",
platformCredential.getVersion(),
hardwareInfo.getVersion());
if (!fieldValidation) {
resultMessage.append("Platform version did not match\n");
if (!fieldValidation) {
resultMessage.append("Platform version did not match\n");
}
passesValidation &= fieldValidation;
} else {
log.warn("The Platform Certificate System version was {} and the reported Device System Information "
+ "version was {}, therefore this check is skipped...",
platformCredential.getVersion(), hardwareInfo.getVersion());
}
passesValidation &= fieldValidation;
// check PlatformSerial against both system-serial-number and baseboard-serial-number
fieldValidation = (
(optionalPlatformCredentialFieldNullOrMatches(
@ -291,42 +299,86 @@ public class CertificateAttributeScvValidator extends SupplyChainCredentialValid
// There is no need to do comparisons with components that are invalid because
// they did not have a manufacturer or model.
List<ComponentIdentifier> validPcComponents = allPcComponents.stream()
.filter(identifier -> identifier.getComponentManufacturer() != null
&& identifier.getComponentModel() != null)
.collect(Collectors.toList());
// List<ComponentIdentifier> validPcComponents = allPcComponents.stream()
// .filter(identifier -> identifier.getComponentManufacturer() != null
// && identifier.getComponentModel() != null)
// .collect(Collectors.toList());
String paccorOutputString = deviceInfoReport.getPaccorOutputString();
String unmatchedComponents;
try {
List<ComponentInfo> componentInfoList
= getComponentInfoFromPaccorOutput(paccorOutputString);
unmatchedComponents = validateV2p0PlatformCredentialComponentsExpectingExactMatch(
platformCredential.getId(),
validPcComponents, componentInfoList);
fieldValidation &= unmatchedComponents.isEmpty();
} catch (IOException e) {
final String baseErrorMessage = "Error parsing JSON output from PACCOR: ";
log.error(baseErrorMessage + e.toString());
log.error("PACCOR output string:\n" + paccorOutputString);
return new AppraisalStatus(ERROR, baseErrorMessage + e.getMessage());
// String paccorOutputString = deviceInfoReport.getPaccorOutputString();
// String unmatchedComponents;
// populate componentResults list
List<ComponentResult> componentResults = componentResultRepository
.findByCertificateSerialNumberAndBoardSerialNumber(
platformCredential.getSerialNumber().toString(),
platformCredential.getPlatformSerial());
// first create hash map based on hashCode
Map<Integer, List<ComponentInfo>> deviceHashMap = new HashMap<>();
componentInfos.stream().forEach((componentInfo) -> {
List<ComponentInfo> innerList;
Integer compInfoHash = componentInfo.hashCommonElements();
if (deviceHashMap.containsKey(compInfoHash)) {
innerList = deviceHashMap.get(compInfoHash);
innerList.add(componentInfo);
} else {
innerList = new ArrayList<>(0);
innerList.add(componentInfo);
}
deviceHashMap.put(compInfoHash, innerList);
});
// Look for hash code in device mapping
// if it exists, don't save the component
List<ComponentResult> remainingComponentResults = new ArrayList<>();
int numOfAttributes = 0;
for (ComponentResult componentResult : componentResults) {
if (!deviceHashMap.containsKey(componentResult.hashCommonElements())) {
// didn't find the component result in the hashed mapping
remainingComponentResults.add(componentResult);
}
}
if (!remainingComponentResults.isEmpty()) {
// continue down the options, move to a different method.
// create component class mapping to component info
Map<String, List<ComponentInfo>> componentDeviceMap = new HashMap<>();
componentInfos.stream().forEach((componentInfo) -> {
List<ComponentInfo> innerList;
String componentClass = componentInfo.getComponentClass();
if (componentDeviceMap.containsKey(componentClass)) {
innerList = componentDeviceMap.get(componentClass);
innerList.add(componentInfo);
} else {
innerList = new ArrayList<>(0);
innerList.add(componentInfo);
}
componentDeviceMap.put(componentClass, innerList);
});
List<ComponentInfo> componentClassInfo;
List<ComponentAttributeResult> attributeResults = new ArrayList<>();
for (ComponentResult componentResult : remainingComponentResults) {
componentClassInfo = componentDeviceMap.get(componentResult.getComponentClassValue());
if (componentClassInfo.size() == 1) {
attributeResults.addAll(generateComponentResults(componentClassInfo.get(0), componentResult));
} else {
attributeResults.addAll(findMismatchedValues(componentClassInfo, componentResult));
}
}
for (ComponentAttributeResult componentAttributeResult : attributeResults) {
componentAttributeResult.setProvisionSessionId(provisionSessionId);
componentAttributeRepository.save(componentAttributeResult);
fieldValidation &= componentAttributeResult.checkMatchedStatus();
}
numOfAttributes = attributeResults.size();
}
StringBuilder additionalInfo = new StringBuilder();
if (!fieldValidation) {
resultMessage.append("There are unmatched components:\n");
resultMessage.append(unmatchedComponents);
// pass information of which ones failed in additionInfo
int counter = 0;
for (ComponentIdentifier ci : validPcComponents) {
counter++;
additionalInfo.append(String.format("%d;", ci.hashCode()));
}
if (counter > 0) {
additionalInfo.insert(0, "COMPID=");
additionalInfo.append(counter);
}
if (!remainingComponentResults.isEmpty()) {
resultMessage.append(String.format("There are %d components not matched%n",
remainingComponentResults.size()));
resultMessage.append(String.format("\twith %d total attributes mismatched.",
numOfAttributes));
}
passesValidation &= fieldValidation;
@ -338,6 +390,78 @@ public class CertificateAttributeScvValidator extends SupplyChainCredentialValid
}
}
/**
* This method produces component attribute results for a single device that was found
* by component class.
* @param componentInfo the device object
* @param componentResult the certificate expected object
* @return a list of attribute match results
*/
private static List<ComponentAttributeResult> generateComponentResults(
final ComponentInfo componentInfo,
final ComponentResult componentResult) {
// there are instances of components with the same class (ie hard disks, memory)
List<ComponentAttributeResult> attributeResults = new ArrayList<>();
if (!componentInfo.getComponentManufacturer().equals(componentResult.getManufacturer())) {
attributeResults.add(new ComponentAttributeResult(componentResult.getId(),
componentResult.getManufacturer(), componentInfo.getComponentManufacturer()));
}
if (!componentInfo.getComponentModel().equals(componentResult.getModel())) {
attributeResults.add(new ComponentAttributeResult(componentResult.getId(),
componentResult.getModel(), componentInfo.getComponentModel()));
}
if (!componentInfo.getComponentSerial().equals(componentResult.getSerialNumber())) {
attributeResults.add(new ComponentAttributeResult(componentResult.getId(),
componentResult.getSerialNumber(), componentInfo.getComponentSerial()));
}
if (!componentInfo.getComponentRevision().equals(componentResult.getRevisionNumber())) {
attributeResults.add(new ComponentAttributeResult(componentResult.getId(),
componentResult.getRevisionNumber(), componentInfo.getComponentRevision()));
}
return attributeResults;
}
/**
* This method is called when there are multiple components on the device that match
* the certificate component's component class type and there is either a mismatch or
* a status of not found to be assigned.
* @param componentClassInfo list of device components with the same class type
* @param componentResult the certificate component that is mismatched
* @return a list of attribute results, if all 4 attributes are never matched, it is not found
*/
private static List<ComponentAttributeResult> findMismatchedValues(
final List<ComponentInfo> componentClassInfo,
final ComponentResult componentResult) {
// this list only has those of the same class type
Map<String, ComponentInfo> componentSerialMap = new HashMap<>();
componentClassInfo.stream().forEach((componentInfo) -> {
componentSerialMap.put(componentInfo.getComponentSerial(), componentInfo);
});
// see if the serial exists
ComponentInfo componentInfo = componentSerialMap.get(componentResult.getSerialNumber());
if (componentInfo != null && componentInfo.getComponentManufacturer()
.equals(componentResult.getManufacturer())) {
// the serial matched and the manufacturer, create attribute result and move on
return generateComponentResults(componentInfo, componentResult);
} else {
// didn't find based on serial
// look for highest match; otherwise ignore
// I already know serial doesn't match
for (ComponentInfo ci : componentClassInfo) {
if (ci.getComponentManufacturer().equals(componentResult.getManufacturer())
&& ci.getComponentModel().equals(componentResult.getModel())) {
return generateComponentResults(ci, componentResult);
}
}
}
return Collections.emptyList();
}
/**
* The main purpose of this method, the in process of validation, is to
* pick out the changes that lead to the delta cert and make sure the changes
@ -351,7 +475,6 @@ public class CertificateAttributeScvValidator extends SupplyChainCredentialValid
*/
@SuppressWarnings("methodlength")
static AppraisalStatus validateDeltaAttributesChainV2p0(
final UUID certificateId,
final DeviceInfoReport deviceInfoReport,
final Map<PlatformCredential, SupplyChainValidation> deltaMapping,
final List<ComponentIdentifier> origPcComponents) {
@ -466,10 +589,11 @@ public class CertificateAttributeScvValidator extends SupplyChainCredentialValid
String unmatchedComponents;
try {
// compare based on component class
List<ComponentInfo> componentInfoList = getV2PaccorOutput(paccorOutputString);
List<ComponentInfo> componentInfoList = getComponentInfoFromPaccorOutput(
deviceInfoReport.getNetworkInfo().getHostname(),
paccorOutputString);
// this is what I want to rewrite
unmatchedComponents = validateV2PlatformCredentialAttributes(
certificateId,
baseCompList,
componentInfoList);
fieldValidation &= unmatchedComponents.isEmpty();
@ -505,7 +629,6 @@ public class CertificateAttributeScvValidator extends SupplyChainCredentialValid
}
private static String validateV2PlatformCredentialAttributes(
final UUID certificateId,
final List<ComponentIdentifier> fullDeltaChainComponents,
final List<ComponentInfo> allDeviceInfoComponents) {
ComponentIdentifierV2 ciV2;
@ -521,7 +644,7 @@ public class CertificateAttributeScvValidator extends SupplyChainCredentialValid
ciV2 = (ComponentIdentifierV2) cId;
if (cInfo.getComponentClass().contains(
ciV2.getComponentClass().getComponentIdentifier())
&& isMatch(certificateId, cId, cInfo)) {
&& isMatch(cId, cInfo)) {
subCompIdList.remove(cId);
subCompInfoList.remove(cInfo);
}
@ -716,10 +839,10 @@ public class CertificateAttributeScvValidator extends SupplyChainCredentialValid
* **NEW** this is updated with just the unmatched components
* if there are any failures, otherwise it remains unchanged.
* @param allDeviceInfoComponents the device info report components
* @return true if validation passes
* @return passes if the returned value is empty, otherwise the components that are unmatched
* populate the string
*/
private static String validateV2p0PlatformCredentialComponentsExpectingExactMatch(
final UUID certificateId,
final List<ComponentIdentifier> untrimmedPcComponents,
final List<ComponentInfo> allDeviceInfoComponents) {
// For each manufacturer listed in the platform credential, create two lists:
@ -791,7 +914,7 @@ public class CertificateAttributeScvValidator extends SupplyChainCredentialValid
if (first.isPresent()) {
ComponentInfo potentialMatch = first.get();
if (isMatch(certificateId, pcComponent, potentialMatch)) {
if (isMatch(pcComponent, potentialMatch)) {
pcComponentsFromManufacturer.remove(pcComponent);
deviceInfoComponentsFromManufacturer.remove(potentialMatch);
}
@ -819,7 +942,7 @@ public class CertificateAttributeScvValidator extends SupplyChainCredentialValid
if (first.isPresent()) {
ComponentInfo potentialMatch = first.get();
if (isMatch(certificateId, pcComponent, potentialMatch)) {
if (isMatch(pcComponent, potentialMatch)) {
pcComponentsFromManufacturer.remove(pcComponent);
deviceInfoComponentsFromManufacturer.remove(potentialMatch);
}
@ -833,7 +956,7 @@ public class CertificateAttributeScvValidator extends SupplyChainCredentialValid
= deviceInfoComponentsFromManufacturer.iterator();
while (diComponentIter.hasNext()) {
ComponentInfo potentialMatch = diComponentIter.next();
if (isMatch(certificateId, ci, potentialMatch)) {
if (isMatch(ci, potentialMatch)) {
pcComponentsFromManufacturer.remove(ci);
diComponentIter.remove();
}
@ -872,13 +995,11 @@ public class CertificateAttributeScvValidator extends SupplyChainCredentialValid
/**
* Checks if the fields in the potentialMatch match the fields in the pcComponent,
* or if the relevant field in the pcComponent is empty.
* @param certificateId the certificate id
* @param pcComponent the platform credential component
* @param potentialMatch the component info from a device info report
* @return true if the fields match exactly (null is considered the same as an empty string)
*/
private static boolean isMatch(final UUID certificateId,
final ComponentIdentifier pcComponent,
public static boolean isMatch(final ComponentIdentifier pcComponent,
final ComponentInfo potentialMatch) {
boolean matchesSoFar = true;
@ -887,49 +1008,24 @@ public class CertificateAttributeScvValidator extends SupplyChainCredentialValid
pcComponent.getComponentManufacturer()
);
if (matchesSoFar) {
componentResultList.add(new ComponentResult(certificateId, pcComponent.hashCode(),
potentialMatch.getComponentSerial(),
pcComponent.getComponentSerial().getString()));
}
matchesSoFar &= isMatchOrEmptyInPlatformCert(
potentialMatch.getComponentModel(),
pcComponent.getComponentModel()
);
if (matchesSoFar) {
componentResultList.add(new ComponentResult(certificateId, pcComponent.hashCode(),
potentialMatch.getComponentSerial(),
pcComponent.getComponentSerial().getString()));
}
matchesSoFar &= isMatchOrEmptyInPlatformCert(
potentialMatch.getComponentSerial(),
pcComponent.getComponentSerial()
);
if (matchesSoFar) {
componentResultList.add(new ComponentResult(certificateId, pcComponent.hashCode(),
potentialMatch.getComponentSerial(),
pcComponent.getComponentSerial().getString()));
}
matchesSoFar &= isMatchOrEmptyInPlatformCert(
potentialMatch.getComponentRevision(),
pcComponent.getComponentRevision()
);
if (matchesSoFar) {
componentResultList.add(new ComponentResult(certificateId, pcComponent.hashCode(),
potentialMatch.getComponentSerial(),
pcComponent.getComponentSerial().getString()));
}
return matchesSoFar;
}
/**
* Checks if the fields in the potentialMatch match the fields in the pcComponent,
* or if the relevant field in the pcComponent is empty.
@ -1009,7 +1105,7 @@ public class CertificateAttributeScvValidator extends SupplyChainCredentialValid
* @param platformSerialNumberDescription description of the serial number for logging purposes.
* @param deviceInfoSerialNumbers the map of device info serial numbers
* (key = description, value = serial number)
* @return true if the platform serial number was found (case insensitive search),
* @return true if the platform serial number was found (case-insensitive search),
* false otherwise
*/
private static boolean deviceInfoContainsPlatformSerialNumber(
@ -1090,6 +1186,22 @@ public class CertificateAttributeScvValidator extends SupplyChainCredentialValid
return false;
}
/**
* Per update to the provisioning via Issue 723, Not Specified and Unknown values
* are to be ignored.
* @param versionNumber string value of the device/platform version number
* @return true if they equal Not Specified or Unknown
*/
public static boolean isNotSpecifiedOrUnknown(final String versionNumber) {
if(versionNumber == null) {
return true;
}
String fieldValue = versionNumber.toLowerCase();
return fieldValue.equals(DeviceInfoEnums.NOT_SPECIFIED.toLowerCase())
|| fieldValue.equals(LC_UNKNOWN);
}
private static boolean platformCredentialFieldMatches(
final String platformCredentialFieldName,
final String platformCredentialFieldValue,

View File

@ -1,7 +1,10 @@
package hirs.attestationca.persist.validation;
import hirs.attestationca.persist.entity.manager.ComponentAttributeRepository;
import hirs.attestationca.persist.entity.manager.ComponentResultRepository;
import hirs.attestationca.persist.entity.userdefined.certificate.EndorsementCredential;
import hirs.attestationca.persist.entity.userdefined.certificate.PlatformCredential;
import hirs.attestationca.persist.entity.userdefined.info.ComponentInfo;
import hirs.attestationca.persist.entity.userdefined.report.DeviceInfoReport;
import hirs.attestationca.persist.enums.AppraisalStatus;
import lombok.extern.log4j.Log4j2;
@ -14,6 +17,8 @@ import java.security.cert.CertificateExpiredException;
import java.security.cert.CertificateNotYetValidException;
import java.security.cert.X509Certificate;
import java.util.Date;
import java.util.List;
import java.util.UUID;
import static hirs.attestationca.persist.enums.AppraisalStatus.Status.ERROR;
import static hirs.attestationca.persist.enums.AppraisalStatus.Status.FAIL;
@ -26,7 +31,7 @@ public class CredentialValidator extends SupplyChainCredentialValidator {
* Checks if the endorsement credential is valid.
*
* @param ec the endorsement credential to verify.
* @param trustStore trust store holding trusted trusted certificates.
* @param trustStore trust store holding trusted certificates.
* @param acceptExpired whether or not to accept expired and not yet valid certificates
* as valid.
* @return the result of the validation.
@ -160,12 +165,19 @@ public class CredentialValidator extends SupplyChainCredentialValidator {
* serial number of the platform to be validated.
* @param endorsementCredential The endorsement credential supplied from the same
* identity request as the platform credential.
* @param componentResultRepository db access to component result of mismatching
* @param componentAttributeRepository db access to component attribute match status
* @param componentInfos list of device components
* @return The result of the validation.
*/
public static AppraisalStatus validatePlatformCredentialAttributes(
final PlatformCredential platformCredential,
final DeviceInfoReport deviceInfoReport,
final EndorsementCredential endorsementCredential) {
final EndorsementCredential endorsementCredential,
final ComponentResultRepository componentResultRepository,
final ComponentAttributeRepository componentAttributeRepository,
final List<ComponentInfo> componentInfos,
final UUID provisionSessionId) {
final String baseErrorMessage = "Can't validate platform credential attributes without ";
String message;
if (platformCredential == null) {
@ -180,6 +192,10 @@ public class CredentialValidator extends SupplyChainCredentialValidator {
message = baseErrorMessage + "an endorsement credential";
return new AppraisalStatus(FAIL, message);
}
if (componentInfos.isEmpty()) {
message = baseErrorMessage + "a list of device components";
return new AppraisalStatus(FAIL, message);
}
// Quick, early check if the platform credential references the endorsement credential
if (!endorsementCredential.getSerialNumber()
@ -192,7 +208,8 @@ public class CredentialValidator extends SupplyChainCredentialValidator {
String credentialType = platformCredential.getCredentialType();
if (PlatformCredential.CERTIFICATE_TYPE_2_0.equals(credentialType)) {
return CertificateAttributeScvValidator.validatePlatformCredentialAttributesV2p0(
platformCredential, deviceInfoReport);
platformCredential, deviceInfoReport, componentResultRepository,
componentAttributeRepository, componentInfos, provisionSessionId);
}
return CertificateAttributeScvValidator.validatePlatformCredentialAttributesV1p2(
platformCredential, deviceInfoReport);

View File

@ -7,7 +7,6 @@ import hirs.attestationca.persist.entity.userdefined.info.ComponentInfo;
import lombok.NoArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.util.Strings;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.cert.CertException;
import org.bouncycastle.cert.X509AttributeCertificateHolder;
@ -32,7 +31,6 @@ import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
@ -93,15 +91,13 @@ public class SupplyChainCredentialValidator {
} else if (trustStore.size() == 0) {
throw new SupplyChainValidatorException("Truststore is empty");
}
} catch (KeyStoreException e) {
log.error("Error accessing trust store: " + e.getMessage());
} catch (KeyStoreException ksEx) {
log.error("Error accessing trust store: " + ksEx.getMessage());
}
try {
Set<X509Certificate> trustedCerts = new HashSet<>();
Enumeration<String> alias = trustStore.aliases();
while (alias.hasMoreElements()) {
trustedCerts.add((X509Certificate) trustStore.getCertificate(alias.nextElement()));
}
@ -111,8 +107,8 @@ public class SupplyChainCredentialValidator {
log.error("Cert chain could not be validated");
}
return certChainValidated;
} catch (KeyStoreException e) {
throw new SupplyChainValidatorException("Error with the trust store", e);
} catch (KeyStoreException ksEx) {
throw new SupplyChainValidatorException("Error with the trust store", ksEx);
}
}
@ -139,8 +135,8 @@ public class SupplyChainCredentialValidator {
} else if (trustStore.size() == 0) {
throw new SupplyChainValidatorException("Truststore is empty");
}
} catch (KeyStoreException e) {
log.error("Error accessing trust store: " + e.getMessage());
} catch (KeyStoreException ksEx) {
log.error("Error accessing trust store: " + ksEx.getMessage());
}
try {
@ -152,9 +148,9 @@ public class SupplyChainCredentialValidator {
}
return validateCertChain(cert, trustedCerts).isEmpty();
} catch (KeyStoreException e) {
log.error("Error accessing keystore", e);
throw new SupplyChainValidatorException("Error with the trust store", e);
} catch (KeyStoreException ksEx) {
log.error("Error accessing keystore", ksEx);
throw new SupplyChainValidatorException("Error with the trust store", ksEx);
}
}
@ -277,11 +273,13 @@ public class SupplyChainCredentialValidator {
/**
* Parses the output from PACCOR's allcomponents.sh script into ComponentInfo objects.
* @param hostName the host machine associated with the component
* @param paccorOutput the output from PACCOR's allcomoponents.sh
* @return a list of ComponentInfo objects built from paccorOutput
* @throws java.io.IOException if something goes wrong parsing the JSON
*/
public static List<ComponentInfo> getComponentInfoFromPaccorOutput(final String paccorOutput)
public static List<ComponentInfo> getComponentInfoFromPaccorOutput(final String hostName,
final String paccorOutput)
throws IOException {
List<ComponentInfo> componentInfoList = new ArrayList<>();
@ -292,54 +290,34 @@ public class SupplyChainCredentialValidator {
= rootNode.findValue("COMPONENTS").elements();
while (jsonComponentNodes.hasNext()) {
JsonNode next = jsonComponentNodes.next();
componentInfoList.add(new ComponentInfo(
getJSONNodeValueAsText(next, "MANUFACTURER"),
getJSONNodeValueAsText(next, "MODEL"),
getJSONNodeValueAsText(next, "SERIAL"),
getJSONNodeValueAsText(next, "REVISION")));
List<JsonNode> compClassNodes = next.findValues("COMPONENTCLASS");
if (compClassNodes.isEmpty()) {
componentInfoList.add(new ComponentInfo(hostName,
getJSONNodeValueAsText(next, "MANUFACTURER"),
getJSONNodeValueAsText(next, "MODEL"),
getJSONNodeValueAsText(next, "SERIAL"),
getJSONNodeValueAsText(next, "REVISION")));
} else {
// version 2
String componentClass = StringUtils.EMPTY;
for (JsonNode subNode : compClassNodes) {
componentClass = getJSONNodeValueAsText(subNode,
"COMPONENTCLASSVALUE");
}
componentInfoList.add(new ComponentInfo(hostName,
getJSONNodeValueAsText(next, "MANUFACTURER"),
getJSONNodeValueAsText(next, "MODEL"),
getJSONNodeValueAsText(next, "SERIAL"),
getJSONNodeValueAsText(next, "REVISION"),
componentClass));
}
}
}
return componentInfoList;
}
/**
* Parses the output from PACCOR's allcomponents.sh script into ComponentInfo objects.
* @param paccorOutput the output from PACCOR's allcomoponents.sh
* @return a list of ComponentInfo objects built from paccorOutput
* @throws IOException if something goes wrong parsing the JSON
*/
public static List<ComponentInfo> getV2PaccorOutput(
final String paccorOutput) throws IOException {
List<ComponentInfo> ciList = new LinkedList<>();
String manufacturer, model, serial, revision;
String componentClass = Strings.EMPTY;
if (StringUtils.isNotEmpty(paccorOutput)) {
ObjectMapper objectMapper = new ObjectMapper(new JsonFactory());
JsonNode rootNode = objectMapper.readTree(paccorOutput);
Iterator<JsonNode> jsonComponentNodes
= rootNode.findValue("COMPONENTS").elements();
while (jsonComponentNodes.hasNext()) {
JsonNode next = jsonComponentNodes.next();
manufacturer = getJSONNodeValueAsText(next, "MANUFACTURER");
model = getJSONNodeValueAsText(next, "MODEL");
serial = getJSONNodeValueAsText(next, "SERIAL");
revision = getJSONNodeValueAsText(next, "REVISION");
List<JsonNode> compClassNodes = next.findValues("COMPONENTCLASS");
for (JsonNode subNode : compClassNodes) {
componentClass = getJSONNodeValueAsText(subNode,
"COMPONENTCLASSVALUE");
}
ciList.add(new ComponentInfo(manufacturer, model,
serial, revision, componentClass));
}
}
return ciList;
}
private static String getJSONNodeValueAsText(final JsonNode node, final String fieldName) {
if (node.hasNonNull(fieldName)) {
return node.findValue(fieldName).textValue();
@ -498,10 +476,10 @@ public class SupplyChainCredentialValidator {
PublicKey key = cert.getPublicKey();
cert.verify(key);
return true;
} catch (SignatureException | InvalidKeyException e) {
} catch (SignatureException | InvalidKeyException ex) {
return false;
} catch (CertificateException | NoSuchAlgorithmException | NoSuchProviderException e) {
log.error("Exception occurred while checking if cert is self-signed", e);
} catch (CertificateException | NoSuchAlgorithmException | NoSuchProviderException ex) {
log.error("Exception occurred while checking if cert is self-signed", ex);
return false;
}
}

View File

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

View File

@ -1,12 +1,14 @@
package hirs.attestationca.persist.entity;
import hirs.attestationca.persist.entity.userdefined.certificate.CertificateAuthorityCredential;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.security.cert.CertificateException;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.*;
/**
* Unit tests for the class <code>Appraiser</code>.
@ -160,5 +162,4 @@ public final class AppraiserTest {
assertNotEquals(appraiser1.hashCode(), appraiser2.hashCode());
assertNotEquals(appraiser2.hashCode(), appraiser1.hashCode());
}
}
}

View File

@ -0,0 +1 @@
package hirs.attestationca.persist.entity;

View File

@ -87,13 +87,16 @@ public class TPM2ProvisionerStateTest {
/**
* Test that {@link TPM2ProvisionerState#getTPM2ProvisionerState(TPM2ProvisionerStateRepository, byte[])} works.
* {@link TPM2ProvisionerState#getTPM2ProvisionerState(TPM2ProvisionerStateRepository, byte[])}, null is returned.
* Test that {@link TPM2ProvisionerState#getTPM2ProvisionerState(
* TPM2ProvisionerStateRepository, byte[])} works.
* {@link TPM2ProvisionerState#getTPM2ProvisionerState(
* TPM2ProvisionerStateRepository, byte[])}, null is returned.
* @throws IOException this will never happen
*/
@Test
public final void testGetTPM2ProvisionerStateNominal() throws IOException {
TPM2ProvisionerStateRepository tpm2ProvisionerStateRepository = mock(TPM2ProvisionerStateRepository.class);
TPM2ProvisionerStateRepository tpm2ProvisionerStateRepository =
mock(TPM2ProvisionerStateRepository.class);
byte[] nonce = new byte[32];
byte[] identityClaim = new byte[360];
random.nextBytes(nonce);
@ -112,12 +115,14 @@ public class TPM2ProvisionerStateTest {
/**
* Test that if a null is passed as a nonce to
* {@link TPM2ProvisionerState#getTPM2ProvisionerState(TPM2ProvisionerStateRepository, byte[])}, null is returned.
* {@link TPM2ProvisionerState#getTPM2ProvisionerState(
* TPM2ProvisionerStateRepository, byte[])}, null is returned.
* @throws IOException this will never happen
*/
@Test
public final void testGetTPM2ProvisionerStateNullNonce() throws IOException {
TPM2ProvisionerStateRepository tpm2ProvisionerStateRepository = mock(TPM2ProvisionerStateRepository.class);
TPM2ProvisionerStateRepository tpm2ProvisionerStateRepository =
mock(TPM2ProvisionerStateRepository.class);
byte[] nonce = new byte[32];
byte[] identityClaim = new byte[360];
random.nextBytes(nonce);
@ -133,12 +138,14 @@ public class TPM2ProvisionerStateTest {
/**
* Test that if a nonce that is less than 8 bytes is passed to
* {@link TPM2ProvisionerState#getTPM2ProvisionerState(TPM2ProvisionerStateRepository, byte[])}, null is returned.
* {@link TPM2ProvisionerState#getTPM2ProvisionerState(
* TPM2ProvisionerStateRepository, byte[])}, null is returned.
* @throws IOException this will never happen
*/
@Test
public final void testGetTPM2ProvisionerStateNonceTooSmall() throws IOException {
TPM2ProvisionerStateRepository tpm2ProvisionerStateRepository = mock(TPM2ProvisionerStateRepository.class);
TPM2ProvisionerStateRepository tpm2ProvisionerStateRepository =
mock(TPM2ProvisionerStateRepository.class);
byte[] nonce = new byte[32];
byte[] identityClaim = new byte[360];
random.nextBytes(nonce);

View File

@ -0,0 +1 @@
package hirs.attestationca.persist.entity.tpm;

View File

@ -0,0 +1,313 @@
package hirs.attestationca.persist.entity.userdefined;
import hirs.attestationca.persist.entity.ArchivableEntity;
import hirs.attestationca.persist.entity.userdefined.certificate.CertificateAuthorityCredential;
import hirs.attestationca.persist.entity.userdefined.certificate.ConformanceCredential;
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.entity.userdefined.info.FirmwareInfo;
import hirs.attestationca.persist.entity.userdefined.info.HardwareInfo;
import hirs.attestationca.persist.entity.userdefined.info.NetworkInfo;
import hirs.attestationca.persist.entity.userdefined.info.OSInfo;
import hirs.attestationca.persist.entity.userdefined.info.TPMInfo;
import hirs.attestationca.persist.entity.userdefined.report.DeviceInfoReport;
import hirs.attestationca.persist.entity.userdefined.report.DeviceInfoReportTest;
import hirs.attestationca.persist.enums.AppraisalStatus;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetAddress;
import java.net.URISyntaxException;
import java.net.UnknownHostException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
/**
* Class with definitions and functions common to multiple Userdefined Entity object tests.
*
*/
public class AbstractUserdefinedEntityTest {
/**
* Location of a test (fake) SGI intermediate CA certificate.
*/
public static final String FAKE_SGI_INT_CA_FILE = "/certificates/fakeSGIIntermediateCA.cer";
/**
* Location of a test (fake) Intel intermediate CA certificate.
*/
public static final String FAKE_INTEL_INT_CA_FILE =
"/certificates/fakeIntelIntermediateCA.cer";
/**
* Location of a test (fake) root CA certificate.
*/
public static final String FAKE_ROOT_CA_FILE = "/certificates/fakeRootCA.cer";
/**
* Hex-encoded subject key identifier for the FAKE_ROOT_CA_FILE.
*/
public static final String FAKE_ROOT_CA_SUBJECT_KEY_IDENTIFIER_HEX =
"58ec313a1699f94c1c8c4e2c6412402b258f0177";
/**
* Location of a test identity certificate.
*/
private static final String TEST_IDENTITY_CERT = "/tpm/sample_identity_cert.cer";
/**
* Location of a test platform attribute cert.
*/
public static final String TEST_PLATFORM_CERT_1 =
"/validation/platform_credentials/Intel_pc1.cer";
/**
* Location of another, slightly different platform attribute cert.
*/
public static final String TEST_PLATFORM_CERT_2 =
"/validation/platform_credentials/Intel_pc2.cer";
/**
* Location of another, slightly different platform attribute cert.
*/
public static final String TEST_PLATFORM_CERT_3 =
"/validation/platform_credentials/Intel_pc3.cer";
/**
* Platform cert with comma separated baseboard and chassis serial number.
*/
public static final String TEST_PLATFORM_CERT_4 =
"/validation/platform_credentials/Intel_pc4.pem";
/**
* Another platform cert with comma separated baseboard and chassis serial number.
*/
public static final String TEST_PLATFORM_CERT_5 =
"/validation/platform_credentials/Intel_pc5.pem";
/**
* Location of another, slightly different platform attribute cert.
*/
public static final String TEST_PLATFORM_CERT_6 =
"/validation/platform_credentials/TPM_INTC_Platform_Cert_RSA.txt";
private static final Logger LOGGER = LogManager.getLogger(DeviceInfoReportTest.class);
/**
* Dummy message for supply chain validation test.
*/
public static final String VALIDATION_MESSAGE = "Some message.";
/**
* Construct a test certificate from the given parameters.
*
* @param <T> the type of Certificate that will be created
* @param certificateClass the class of certificate to generate
* @param filename the location of the certificate to be used
* @return the newly-constructed Certificate
* @throws IOException if there is a problem constructing the test certificate
*/
public static <T extends ArchivableEntity> Certificate getTestCertificate(
final Class<T> certificateClass, final String filename)
throws IOException {
return getTestCertificate(certificateClass, filename, null, null);
}
/**
* Construct a test certificate from the given parameters.
*
* @param <T> the type of Certificate that will be created
* @param certificateClass the class of certificate to generate
* @param filename the location of the certificate to be used
* @param endorsementCredential the endorsement credentials (can be null)
* @param platformCredentials the platform credentials (can be null)
* @return the newly-constructed Certificate
* @throws IOException if there is a problem constructing the test certificate
*/
public static <T extends ArchivableEntity> Certificate getTestCertificate(
final Class<T> certificateClass, final String filename,
final EndorsementCredential endorsementCredential,
final List<PlatformCredential> platformCredentials)
throws IOException {
Path certPath;
try {
certPath = Paths.get(Objects.requireNonNull(
AbstractUserdefinedEntityTest.class.getResource(filename)).toURI());
// certPath = Paths.get(Objects.requireNonNull(
// CertificateTest.class.getResource(filename)).toURI());
} catch (URISyntaxException e) {
throw new IOException("Could not resolve path URI", e);
}
switch (certificateClass.getSimpleName()) {
case "CertificateAuthorityCredential":
return new CertificateAuthorityCredential(certPath);
case "ConformanceCredential":
return new ConformanceCredential(certPath);
case "EndorsementCredential":
return new EndorsementCredential(certPath);
case "PlatformCredential":
return new PlatformCredential(certPath);
case "IssuedAttestationCertificate":
return new IssuedAttestationCertificate(certPath,
endorsementCredential, platformCredentials);
default:
throw new IllegalArgumentException(
String.format("Unknown certificate class %s", certificateClass.getName())
);
}
}
/**
* Return a list of all test certificates.
*
* @return a list of all test certificates
* @throws IOException if there is a problem deserializing certificates
*/
public static List<ArchivableEntity> getAllTestCertificates() throws IOException {
return Arrays.asList(
getTestCertificate(CertificateAuthorityCredential.class, FAKE_SGI_INT_CA_FILE),
getTestCertificate(CertificateAuthorityCredential.class, FAKE_INTEL_INT_CA_FILE),
getTestCertificate(CertificateAuthorityCredential.class, FAKE_ROOT_CA_FILE)
);
}
/**
* Creates a DeviceInfoReport instance usable for testing.
*
* @return a test DeviceInfoReport
*/
public static DeviceInfoReport getTestDeviceInfoReport() {
return new DeviceInfoReport(
createTestNetworkInfo(), createTestOSInfo(), createTestFirmwareInfo(),
createTestHardwareInfo(), createTPMInfo()
);
}
/**
* Creates a test instance of NetworkInfo.
*
* @return network information for a fake device
*/
public static NetworkInfo createTestNetworkInfo() {
try {
final String hostname = "test.hostname";
final InetAddress ipAddress =
InetAddress.getByAddress(new byte[] {127, 0, 0, 1});
final byte[] macAddress = new byte[] {11, 22, 33, 44, 55, 66};
return new NetworkInfo(hostname, ipAddress, macAddress);
} catch (UnknownHostException e) {
LOGGER.error("error occurred while creating InetAddress");
return null;
}
}
/**
* Creates a test instance of OSInfo.
*
* @return OS information for a fake device
*/
public static OSInfo createTestOSInfo() {
return new OSInfo("test os name", "test os version", "test os arch",
"test distribution", "test distribution release");
}
/**
* Creates a test instance of FirmwareInfo.
*
* @return Firmware information for a fake device
*/
public static FirmwareInfo createTestFirmwareInfo() {
return new FirmwareInfo("test bios vendor", "test bios version", "test bios release date");
}
/**
* Creates a test instance of HardwareInfo.
*
* @return Hardware information for a fake device
*/
public static HardwareInfo createTestHardwareInfo() {
return new HardwareInfo("test manufacturer", "test product name", "test version",
"test really long serial number with many characters", "test really long chassis "
+ "serial number with many characters",
"test really long baseboard serial number with many characters");
}
/**
* Creates a test instance of TPMInfo.
*
* @return TPM information for a fake device
*/
public static final TPMInfo createTPMInfo() {
final short num1 = 1;
final short num2 = 2;
final short num3 = 3;
final short num4 = 4;
return new TPMInfo("test os make", num1, num2, num3, num4,
getTestIdentityCertificate());
}
/**
* Creates a test identity certificate.
*
* @return the test X509 certificate
*/
public static X509Certificate getTestIdentityCertificate() {
X509Certificate certificateValue = null;
InputStream istream = null;
istream = AbstractUserdefinedEntityTest.class.getResourceAsStream(
TEST_IDENTITY_CERT
);
try {
if (istream == null) {
throw new FileNotFoundException(TEST_IDENTITY_CERT);
}
CertificateFactory cf = CertificateFactory.getInstance("X.509");
certificateValue = (X509Certificate) cf.generateCertificate(
istream);
} catch (Exception e) {
return null;
} finally {
if (istream != null) {
try {
istream.close();
} catch (IOException e) {
LOGGER.error("test certificate file could not be closed");
}
}
}
return certificateValue;
}
/**
* Construct a SupplyChainValidation for use in tests according to the provided parameters.
*
* @param type the type of validation
* @param result the appraisal result
* @param certificates the certificates related to this validation
* @return the resulting SupplyChainValidation object
*/
public static SupplyChainValidation getTestSupplyChainValidation(
final SupplyChainValidation.ValidationType type,
final AppraisalStatus.Status result,
final List<ArchivableEntity> certificates) {
return new SupplyChainValidation(
type,
result,
certificates,
VALIDATION_MESSAGE
);
}
}

View File

@ -1,6 +1,11 @@
package hirs.attestationca.persist.entity.userdefined;
import hirs.attestationca.persist.entity.ArchivableEntity;
import hirs.attestationca.persist.entity.userdefined.certificate.CertificateAuthorityCredential;
import hirs.attestationca.persist.entity.userdefined.certificate.ConformanceCredential;
import hirs.attestationca.persist.entity.userdefined.certificate.EndorsementCredential;
import hirs.attestationca.persist.entity.userdefined.certificate.PlatformCredential;
import org.bouncycastle.cert.X509AttributeCertificateHolder;
import org.junit.jupiter.api.Test;
import java.io.FileInputStream;
import java.io.IOException;
@ -14,12 +19,8 @@ import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import hirs.attestationca.persist.entity.userdefined.certificate.*;
import org.bouncycastle.cert.X509AttributeCertificateHolder;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
@ -29,17 +30,7 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
/**
* This class tests functionality of the {@link Certificate} class.
*/
public class CertificateTest {
/**
* Location of a test (fake) root CA certificate.
*/
public static final String FAKE_ROOT_CA_FILE = "/certificates/fakeRootCA.cer";
/**
* Location of a test (fake) Intel intermediate CA certificate.
*/
public static final String FAKE_INTEL_INT_CA_FILE =
"/certificates/fakeIntelIntermediateCA.cer";
public class CertificateTest extends AbstractUserdefinedEntityTest {
/**
* Location of a test (fake) Intel intermediate CA certificate.
@ -47,11 +38,6 @@ public class CertificateTest {
public static final String INTEL_INT_CA_FILE =
"/validation/platform_credentials/intel_chain/root/intermediate2.cer";
/**
* Location of a test (fake) SGI intermediate CA certificate.
*/
public static final String FAKE_SGI_INT_CA_FILE = "/certificates/fakeSGIIntermediateCA.cer";
/**
* Location of another test self-signed certificate.
*/
@ -78,12 +64,6 @@ public class CertificateTest {
*/
public static final String GS_ROOT_CA = "/certificates/stMicroCaCerts/gstpmroot.crt";
/**
* Hex-encoded subject key identifier for the FAKE_ROOT_CA_FILE.
*/
public static final String FAKE_ROOT_CA_SUBJECT_KEY_IDENTIFIER_HEX =
"58ec313a1699f94c1c8c4e2c6412402b258f0177";
/**
* Location of a test STM endorsement credential.
*/
@ -119,7 +99,8 @@ public class CertificateTest {
public void testConstructCertFromByteArray() throws IOException, URISyntaxException {
Certificate certificate = new CertificateAuthorityCredential(
Files.readAllBytes(
Paths.get(Objects.requireNonNull(this.getClass().getResource(FAKE_ROOT_CA_FILE)).toURI())
Paths.get(Objects.requireNonNull(this.getClass().getResource(
FAKE_ROOT_CA_FILE)).toURI())
)
);
assertEquals(
@ -163,7 +144,8 @@ public class CertificateTest {
@Test
public void testConstructCertFromPath() throws URISyntaxException, IOException {
Certificate certificate = new CertificateAuthorityCredential(
Paths.get(Objects.requireNonNull(this.getClass().getResource(FAKE_ROOT_CA_FILE)).toURI())
Paths.get(Objects.requireNonNull(this.getClass().getResource(
FAKE_ROOT_CA_FILE)).toURI())
);
assertEquals(
"CN=Fake Root CA",
@ -202,12 +184,12 @@ public class CertificateTest {
Certificate.CertificateType.X509_CERTIFICATE,
getTestCertificate(
PlatformCredential.class,
PlatformCredentialTest.TEST_PLATFORM_CERT_3).getCertificateType());
TEST_PLATFORM_CERT_3).getCertificateType());
assertEquals(
Certificate.CertificateType.ATTRIBUTE_CERTIFICATE,
getTestCertificate(
PlatformCredential.class,
PlatformCredentialTest.TEST_PLATFORM_CERT_3).getCertificateType());
TEST_PLATFORM_CERT_3).getCertificateType());
}
@ -220,7 +202,7 @@ public class CertificateTest {
@Test
public void testImportPem() throws IOException {
Certificate platformCredential = getTestCertificate(
PlatformCredential.class, PlatformCredentialTest.TEST_PLATFORM_CERT_4
PlatformCredential.class, TEST_PLATFORM_CERT_4
);
assertEquals(
@ -232,7 +214,7 @@ public class CertificateTest {
);
platformCredential = getTestCertificate(
PlatformCredential.class, PlatformCredentialTest.TEST_PLATFORM_CERT_5
PlatformCredential.class, TEST_PLATFORM_CERT_5
);
assertEquals(
@ -295,13 +277,12 @@ public class CertificateTest {
public void testX509AttributeCertificateParsing() throws IOException, URISyntaxException {
Certificate platformCert = getTestCertificate(
PlatformCredential.class,
PlatformCredentialTest.TEST_PLATFORM_CERT_3
TEST_PLATFORM_CERT_3
);
X509AttributeCertificateHolder attrCertHolder = new X509AttributeCertificateHolder(
Files.readAllBytes(Paths.get(Objects.requireNonNull(this.getClass().getResource(
PlatformCredentialTest.TEST_PLATFORM_CERT_3
)).toURI()))
TEST_PLATFORM_CERT_3)).toURI()))
);
assertEquals(
@ -330,7 +311,7 @@ public class CertificateTest {
public void testX509AttributeCertificateParsingExtended()
throws IOException, URISyntaxException {
Certificate platformCert = getTestCertificate(
PlatformCredential.class, PlatformCredentialTest.TEST_PLATFORM_CERT_6);
PlatformCredential.class, TEST_PLATFORM_CERT_6);
assertEquals("https://trustedservices.intel.com/"
+ "content/TSC/certs/TSC_IssuingCAIKGF_TEST.cer\n",
@ -428,11 +409,13 @@ public class CertificateTest {
assertEquals(
new CertificateAuthorityCredential(
Paths.get(Objects.requireNonNull(this.getClass().getResource(FAKE_ROOT_CA_FILE)).toURI())
Paths.get(Objects.requireNonNull(this.getClass().getResource(
FAKE_ROOT_CA_FILE)).toURI())
),
new CertificateAuthorityCredential(
Files.readAllBytes(
Paths.get(Objects.requireNonNull(this.getClass().getResource(FAKE_ROOT_CA_FILE)).toURI())
Paths.get(Objects.requireNonNull(this.getClass().getResource(
FAKE_ROOT_CA_FILE)).toURI())
)
)
);
@ -450,7 +433,7 @@ public class CertificateTest {
assertNotEquals(
null,
getTestCertificate(CertificateAuthorityCredential.class, FAKE_ROOT_CA_FILE)
);
);
}
/**
@ -489,11 +472,13 @@ public class CertificateTest {
assertEquals(
new CertificateAuthorityCredential(
Paths.get(Objects.requireNonNull(this.getClass().getResource(FAKE_ROOT_CA_FILE)).toURI())
Paths.get(Objects.requireNonNull(this.getClass().getResource(
FAKE_ROOT_CA_FILE)).toURI())
).hashCode(),
new CertificateAuthorityCredential(
Files.readAllBytes(
Paths.get(Objects.requireNonNull(this.getClass().getResource(FAKE_ROOT_CA_FILE)).toURI())
Paths.get(Objects.requireNonNull(this.getClass().getResource(
FAKE_ROOT_CA_FILE)).toURI())
)
).hashCode()
);
@ -520,79 +505,6 @@ public class CertificateTest {
return getTestCertificate(CertificateAuthorityCredential.class, filename);
}
/**
* Construct a test certificate from the given parameters.
*
* @param <T> the type of Certificate that will be created
* @param certificateClass the class of certificate to generate
* @param filename the location of the certificate to be used
* @return the newly-constructed Certificate
* @throws IOException if there is a problem constructing the test certificate
*/
public static <T extends ArchivableEntity> Certificate getTestCertificate(
final Class<T> certificateClass, final String filename)
throws IOException {
return getTestCertificate(certificateClass, filename, null, null);
}
/**
* Construct a test certificate from the given parameters.
*
* @param <T> the type of Certificate that will be created
* @param certificateClass the class of certificate to generate
* @param filename the location of the certificate to be used
* @param endorsementCredential the endorsement credentials (can be null)
* @param platformCredentials the platform credentials (can be null)
* @return the newly-constructed Certificate
* @throws IOException if there is a problem constructing the test certificate
*/
public static <T extends ArchivableEntity> Certificate getTestCertificate(
final Class<T> certificateClass, final String filename,
final EndorsementCredential endorsementCredential,
final List<PlatformCredential> platformCredentials)
throws IOException {
Path certPath;
try {
certPath = Paths.get(Objects.requireNonNull(CertificateTest.class.getResource(filename)).toURI());
} catch (URISyntaxException e) {
throw new IOException("Could not resolve path URI", e);
}
switch (certificateClass.getSimpleName()) {
case "CertificateAuthorityCredential":
return new CertificateAuthorityCredential(certPath);
case "ConformanceCredential":
return new ConformanceCredential(certPath);
case "EndorsementCredential":
return new EndorsementCredential(certPath);
case "PlatformCredential":
return new PlatformCredential(certPath);
case "IssuedAttestationCertificate":
return new IssuedAttestationCertificate(certPath,
endorsementCredential, platformCredentials);
default:
throw new IllegalArgumentException(
String.format("Unknown certificate class %s", certificateClass.getName())
);
}
}
/**
* Return a list of all test certificates.
*
* @return a list of all test certificates
* @throws IOException if there is a problem deserializing certificates
*/
public static List<ArchivableEntity> getAllTestCertificates() throws IOException {
return Arrays.asList(
getTestCertificate(CertificateAuthorityCredential.class, FAKE_SGI_INT_CA_FILE),
getTestCertificate(CertificateAuthorityCredential.class, FAKE_INTEL_INT_CA_FILE),
getTestCertificate(CertificateAuthorityCredential.class, FAKE_ROOT_CA_FILE)
);
}
private static X509Certificate readX509Certificate(final String resourceName)
throws IOException {
@ -603,12 +515,13 @@ public class CertificateTest {
throw new IOException("Cannot get X509 CertificateFactory instance", e);
}
try (FileInputStream certInputStream = new FileInputStream(
Paths.get(Objects.requireNonNull(CertificateTest.class.getResource(resourceName)).toURI()).toFile()
try (FileInputStream certInputStream = new FileInputStream(Paths.get(
Objects.requireNonNull(CertificateTest.class.getResource(
resourceName)).toURI()).toFile()
)) {
return (X509Certificate) cf.generateCertificate(certInputStream);
} catch (CertificateException | URISyntaxException e) {
throw new IOException("Cannot read certificate", e);
}
}
}
}

View File

@ -1,7 +1,6 @@
package hirs.attestationca.persist.entity.userdefined;
import hirs.attestationca.persist.entity.userdefined.report.DeviceInfoReport;
import hirs.attestationca.persist.entity.userdefined.report.DeviceInfoReportTest;
import hirs.attestationca.persist.enums.AppraisalStatus;
import hirs.attestationca.persist.enums.HealthStatus;
import org.junit.jupiter.api.Test;
@ -14,19 +13,7 @@ import static org.junit.jupiter.api.Assertions.assertNull;
* This is the test class for the <code>Device</code> class.
*
*/
public final class DeviceTest {
/**
* Utility method for getting a <code>Device</code> that can be used for
* testing.
*
* @param name name for the <code>Device</code>
*
* @return device
*/
public static Device getTestDevice(final String name) {
final DeviceInfoReport deviceInfo = DeviceInfoReportTest.getTestReport();
return new Device(name, deviceInfo, HealthStatus.UNKNOWN, AppraisalStatus.Status.UNKNOWN, null, false, null, null);
}
public final class DeviceTest extends AbstractUserdefinedEntityTest {
/**
* Tests that the device constructor can take a name.
@ -34,7 +21,9 @@ public final class DeviceTest {
@Test
public void testDevice() {
final String name = "my-laptop";
final Device device = new Device(name, null, HealthStatus.UNKNOWN, AppraisalStatus.Status.UNKNOWN, null, false, null , null);
final Device device = new Device(name, null, HealthStatus.UNKNOWN,
AppraisalStatus.Status.UNKNOWN, null, false,
null, null);
assertNotNull(device);
}
@ -45,8 +34,10 @@ public final class DeviceTest {
@Test
public void testDeviceNameAndInfo() {
final String name = "my-laptop";
final DeviceInfoReport deviceInfo = DeviceInfoReportTest.getTestReport();
new Device(name, deviceInfo, HealthStatus.UNKNOWN, AppraisalStatus.Status.UNKNOWN, null, false, null, null);
final DeviceInfoReport deviceInfo = getTestDeviceInfoReport();
new Device(name, deviceInfo, HealthStatus.UNKNOWN,
AppraisalStatus.Status.UNKNOWN, null, false,
null, null);
}
/**
@ -56,7 +47,9 @@ public final class DeviceTest {
public void testDeviceNameAndNullInfo() {
final String name = "my-laptop";
final DeviceInfoReport deviceInfo = null;
new Device(name, deviceInfo, HealthStatus.UNKNOWN, AppraisalStatus.Status.UNKNOWN, null, false, null, null);
new Device(name, deviceInfo, HealthStatus.UNKNOWN,
AppraisalStatus.Status.UNKNOWN, null, false,
null, null);
}
/**
@ -65,8 +58,10 @@ public final class DeviceTest {
@Test
public void testGetDeviceInfo() {
final String name = "my-laptop";
final DeviceInfoReport deviceInfo = DeviceInfoReportTest.getTestReport();
final Device device = new Device(name, deviceInfo, HealthStatus.UNKNOWN, AppraisalStatus.Status.UNKNOWN, null, false, null, null);
final DeviceInfoReport deviceInfo = getTestDeviceInfoReport();
final Device device = new Device(name, deviceInfo, HealthStatus.UNKNOWN,
AppraisalStatus.Status.UNKNOWN, null, false,
null, null);
assertEquals(deviceInfo, device.getDeviceInfo());
}
@ -76,9 +71,11 @@ public final class DeviceTest {
@Test
public void testSetDeviceInfo() {
final String name = "my-laptop";
final Device device = new Device(name, null, HealthStatus.UNKNOWN, AppraisalStatus.Status.UNKNOWN, null, false, null, null);
final Device device = new Device(name, null, HealthStatus.UNKNOWN,
AppraisalStatus.Status.UNKNOWN, null, false,
null, null);
assertNull(device.getDeviceInfo());
final DeviceInfoReport deviceInfo = DeviceInfoReportTest.getTestReport();
final DeviceInfoReport deviceInfo = getTestDeviceInfoReport();
device.setDeviceInfo(deviceInfo);
assertEquals(deviceInfo, device.getDeviceInfo());
}
@ -89,8 +86,10 @@ public final class DeviceTest {
@Test
public void testSetNullDeviceInfo() {
final String name = "my-laptop";
final DeviceInfoReport deviceInfo = DeviceInfoReportTest.getTestReport();
final Device device = new Device(name, deviceInfo, HealthStatus.UNKNOWN, AppraisalStatus.Status.UNKNOWN, null, false, null, null);
final DeviceInfoReport deviceInfo = getTestDeviceInfoReport();
final Device device = new Device(name, deviceInfo, HealthStatus.UNKNOWN,
AppraisalStatus.Status.UNKNOWN, null, false,
null, null);
assertEquals(deviceInfo, device.getDeviceInfo());
device.setDeviceInfo(null);
assertNull(device.getDeviceInfo());
@ -102,8 +101,10 @@ public final class DeviceTest {
@Test
public void testNotNullLastReportTimeStamp() {
final String name = "my-laptop";
final DeviceInfoReport deviceInfo = DeviceInfoReportTest.getTestReport();
final Device device = new Device(name, deviceInfo, HealthStatus.UNKNOWN, AppraisalStatus.Status.UNKNOWN, null, false, null, null);
final DeviceInfoReport deviceInfo = getTestDeviceInfoReport();
final Device device = new Device(name, deviceInfo, HealthStatus.UNKNOWN,
AppraisalStatus.Status.UNKNOWN, null, false,
null, null);
assertNotNull(device.getLastReportTimestamp());
}
@ -112,7 +113,9 @@ public final class DeviceTest {
*/
@Test
public void testSetHealthStatus() {
final Device device = new Device("test-device", null, HealthStatus.UNKNOWN, AppraisalStatus.Status.UNKNOWN, null, false, null, null);
final Device device = new Device("test-device", null, HealthStatus.UNKNOWN,
AppraisalStatus.Status.UNKNOWN, null, false,
null, null);
device.setHealthStatus(HealthStatus.TRUSTED);
assertEquals(HealthStatus.TRUSTED, device.getHealthStatus());
}
@ -124,9 +127,13 @@ public final class DeviceTest {
public void testDeviceEquals() {
final String name = "my-laptop";
final String otherName = "my-laptop";
final DeviceInfoReport deviceInfo = DeviceInfoReportTest.getTestReport();
final Device device = new Device(name, deviceInfo, HealthStatus.UNKNOWN, AppraisalStatus.Status.UNKNOWN, null, false, null, null);
final Device other = new Device(otherName, deviceInfo, HealthStatus.UNKNOWN, AppraisalStatus.Status.UNKNOWN, null, false, null, null);
final DeviceInfoReport deviceInfo = getTestDeviceInfoReport();
final Device device = new Device(name, deviceInfo, HealthStatus.UNKNOWN,
AppraisalStatus.Status.UNKNOWN, null, false,
null, null);
final Device other = new Device(otherName, deviceInfo, HealthStatus.UNKNOWN,
AppraisalStatus.Status.UNKNOWN, null, false,
null, null);
assertEquals(device, other);
}
@ -136,8 +143,10 @@ public final class DeviceTest {
@Test
public void testGetDefaultSupplyChainStatus() {
String name = "my-laptop";
DeviceInfoReport deviceInfo = DeviceInfoReportTest.getTestReport();
final Device device = new Device(name, deviceInfo, HealthStatus.UNKNOWN, AppraisalStatus.Status.UNKNOWN, null, false, null, null);
final DeviceInfoReport deviceInfo = getTestDeviceInfoReport();
final Device device = new Device(name, deviceInfo, HealthStatus.UNKNOWN,
AppraisalStatus.Status.UNKNOWN, null, false,
null, null);
assertEquals(AppraisalStatus.Status.UNKNOWN, device.getSupplyChainValidationStatus());
}
@ -147,8 +156,10 @@ public final class DeviceTest {
@Test
public void testSetAndGetSupplyChainStatus() {
String name = "my-laptop";
DeviceInfoReport deviceInfo = DeviceInfoReportTest.getTestReport();
final Device device = new Device(name, deviceInfo, HealthStatus.UNKNOWN, AppraisalStatus.Status.UNKNOWN, null, false, null, null);
final DeviceInfoReport deviceInfo = getTestDeviceInfoReport();
final Device device = new Device(name, deviceInfo, HealthStatus.UNKNOWN,
AppraisalStatus.Status.UNKNOWN, null, false,
null, null);
device.setSupplyChainValidationStatus(AppraisalStatus.Status.PASS);
assertEquals(AppraisalStatus.Status.PASS, device.getSupplyChainValidationStatus());
}

View File

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

View File

@ -0,0 +1,221 @@
package hirs.attestationca.persist.entity.userdefined;
import hirs.attestationca.persist.entity.ArchivableEntity;
import hirs.attestationca.persist.entity.userdefined.report.DeviceInfoReport;
import hirs.attestationca.persist.enums.AppraisalStatus;
import hirs.attestationca.persist.enums.HealthStatus;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
/**
* Tests the functionality in SupplyChainValidationSummary.
*/
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class SupplyChainValidationSummaryTest extends AbstractUserdefinedEntityTest {
/**
* Test device.
*
*/
private Device device;
/**
* List of test certificates.
*
*/
private List<ArchivableEntity> certificates;
/**
* Create a set of certificates and a device for use by these tests.
*
* @throws Exception if there is a problem deserializing certificates or creating test device
*/
@BeforeAll
public void setup() throws Exception {
certificates = getAllTestCertificates();
device = getTestDevice("TestDevice");
}
/**
* Tests that an empty summary behaves as expected.
*/
@Test
public void testEmptySummary() throws InterruptedException {
SupplyChainValidationSummary emptySummary = getTestSummary(
0,
0
);
//assertEquals(device, emptySummary.getDevice());
assertEquals(device.getDeviceInfo(), emptySummary.getDevice().getDeviceInfo());
assertEquals(Collections.EMPTY_SET, emptySummary.getValidations());
assertEquals(AppraisalStatus.Status.PASS, emptySummary.getOverallValidationResult());
assertNotNull(emptySummary.getCreateTime());
}
/**
* Test that a summary can't be created with a null validationIdentifier.
*/
@Test
public void testNullValidationIdentifier() {
assertThrows(IllegalArgumentException.class, () ->
new SupplyChainValidationSummary(null, Collections.emptyList()));
}
/**
* Test that a summary can't be created with a null validations list.
*/
@Test
public void testNullValidationList() {
assertThrows(IllegalArgumentException.class, () ->
new SupplyChainValidationSummary(device, null));
}
/**
* Test that summaries with one and two component validations, which both represent successful
* validations, have getters that return the expected information.
*/
@Test
public void testSuccessfulSummary() throws InterruptedException {
SupplyChainValidationSummary oneValidation = getTestSummary(
1,
0
);
//assertEquals(device, oneValidation.getDevice());
assertEquals(device.getDeviceInfo(), oneValidation.getDevice().getDeviceInfo());
assertEquals(1, oneValidation.getValidations().size());
assertEquals(AppraisalStatus.Status.PASS, oneValidation.getOverallValidationResult());
assertNotNull(oneValidation.getCreateTime());
SupplyChainValidationSummary twoValidations = getTestSummary(
2,
0
);
//assertEquals(device, twoValidations.getDevice());
assertEquals(device.getDeviceInfo(), twoValidations.getDevice().getDeviceInfo());
assertEquals(2, twoValidations.getValidations().size());
assertEquals(twoValidations.getOverallValidationResult(), AppraisalStatus.Status.PASS);
assertNotNull(twoValidations.getCreateTime());
}
/**
* Test that summaries with one and two component validations, of which one represents an
* unsuccessful validations, have getters that return the expected information.
*/
@Test
public void testUnsuccessfulSummary() throws InterruptedException {
SupplyChainValidationSummary oneValidation = getTestSummary(
1,
1
);
//assertEquals(device, oneValidation.getDevice());
assertEquals(device.getDeviceInfo(), oneValidation.getDevice().getDeviceInfo());
assertEquals(1, oneValidation.getValidations().size());
assertEquals(AppraisalStatus.Status.FAIL, oneValidation.getOverallValidationResult());
assertNotNull(oneValidation.getCreateTime());
SupplyChainValidationSummary twoValidations = getTestSummary(
2,
1
);
//assertEquals(device, twoValidations.getDevice());
assertEquals(device.getDeviceInfo(), twoValidations.getDevice().getDeviceInfo());
assertEquals(2, twoValidations.getValidations().size());
assertEquals(AppraisalStatus.Status.FAIL, twoValidations.getOverallValidationResult());
assertNotNull(twoValidations.getCreateTime());
SupplyChainValidationSummary twoBadValidations = getTestSummary(
2,
2
);
//assertEquals(device, twoBadValidations.getDevice());
assertEquals(device.getDeviceInfo(), twoBadValidations.getDevice().getDeviceInfo());
assertEquals(2, twoBadValidations.getValidations().size());
assertEquals(AppraisalStatus.Status.FAIL, twoBadValidations.getOverallValidationResult());
assertNotNull(twoBadValidations.getCreateTime());
}
/**
* Utility method for getting a <code>Device</code> that can be used for
* testing.
*
* @param name name for the <code>Device</code>
*
* @return device
*/
public static Device getTestDevice(final String name) {
final DeviceInfoReport deviceInfo = getTestDeviceInfoReport();
return new Device(name, deviceInfo, HealthStatus.UNKNOWN,
AppraisalStatus.Status.UNKNOWN, null,
false, null, null);
}
/**
* Utility method for getting a <code>SupplyChainValidationSummary</code> that can be used for
* testing.
*
* @param numberOfValidations number of validations for the <code>SupplyChainValidationSummary</code>
* @param numFail number of failed validations
*
* @return device
*/
private SupplyChainValidationSummary getTestSummary(
final int numberOfValidations,
final int numFail
) throws InterruptedException {
SupplyChainValidation.ValidationType[] validationTypes =
SupplyChainValidation.ValidationType.values();
if (numberOfValidations > validationTypes.length) {
throw new IllegalArgumentException(String.format(
"Cannot have more than %d validation types",
validationTypes.length
));
}
if (numFail > numberOfValidations) {
throw new IllegalArgumentException(String.format(
"Cannot have more than %d failed validations",
validationTypes.length
));
}
Collection<SupplyChainValidation> validations = new HashSet<>();
for (int i = 0; i < numberOfValidations; i++) {
boolean successful = true;
if (i >= (numberOfValidations - numFail)) {
successful = false;
}
AppraisalStatus.Status result = AppraisalStatus.Status.FAIL;
if (successful) {
result = AppraisalStatus.Status.PASS;
}
validations.add(SupplyChainValidationTest.getTestSupplyChainValidation(
validationTypes[i],
result,
certificates
));
}
return new SupplyChainValidationSummary(device, validations);
}
}

View File

@ -1,21 +1,18 @@
package hirs.attestationca.persist.entity.userdefined;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import hirs.attestationca.persist.entity.ArchivableEntity;
import hirs.attestationca.persist.enums.AppraisalStatus;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
/**
* Simple tests for the {@link SupplyChainValidation} class. Tests for the persistence of this
* class are located in { SupplyChainValidationSummaryTest}.
*/
class SupplyChainValidationTest {
private static final String MESSAGE = "Some message.";
class SupplyChainValidationTest extends AbstractUserdefinedEntityTest {
/**
* Test that this class' getter methods work properly.
@ -31,9 +28,9 @@ class SupplyChainValidationTest {
);
assertEquals(
validation.getCertificatesUsed(),
CertificateTest.getAllTestCertificates()
getAllTestCertificates()
);
assertEquals(validation.getMessage(), MESSAGE);
assertEquals(validation.getMessage(), VALIDATION_MESSAGE);
}
/**
@ -47,8 +44,8 @@ class SupplyChainValidationTest {
new SupplyChainValidation(
null,
AppraisalStatus.Status.PASS,
CertificateTest.getAllTestCertificates(),
MESSAGE
getAllTestCertificates(),
VALIDATION_MESSAGE
));
}
@ -64,7 +61,7 @@ class SupplyChainValidationTest {
SupplyChainValidation.ValidationType.ENDORSEMENT_CREDENTIAL,
AppraisalStatus.Status.PASS,
null,
MESSAGE
VALIDATION_MESSAGE
));
}
@ -78,8 +75,8 @@ class SupplyChainValidationTest {
new SupplyChainValidation(
SupplyChainValidation.ValidationType.ENDORSEMENT_CREDENTIAL,
AppraisalStatus.Status.PASS,
CertificateTest.getAllTestCertificates(),
MESSAGE
getAllTestCertificates(),
VALIDATION_MESSAGE
);
}
@ -95,27 +92,7 @@ class SupplyChainValidationTest {
return getTestSupplyChainValidation(
SupplyChainValidation.ValidationType.ENDORSEMENT_CREDENTIAL,
AppraisalStatus.Status.PASS,
CertificateTest.getAllTestCertificates()
getAllTestCertificates()
);
}
/**
* Construct a SupplyChainValidation for use in tests according to the provided parameters.
*
* @param type the type of validation
* @param result the appraisal result
* @param certificates the certificates related to this validation
* @return the resulting SupplyChainValidation object
*/
public static SupplyChainValidation getTestSupplyChainValidation(
final SupplyChainValidation.ValidationType type,
final AppraisalStatus.Status result,
final List<ArchivableEntity> certificates) {
return new SupplyChainValidation(
type,
result,
certificates,
MESSAGE
);
}
}
}

View File

@ -1,8 +1,7 @@
package hirs.attestationca.persist.entity.userdefined.certificate;
import hirs.attestationca.persist.entity.userdefined.CertificateTest;
import hirs.attestationca.persist.entity.userdefined.AbstractUserdefinedEntityTest;
import org.apache.commons.codec.binary.Hex;
import static org.mockito.Mockito.mock;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import org.junit.jupiter.api.Test;
@ -12,13 +11,11 @@ import java.net.URISyntaxException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.cert.CertificateException;
import hirs.attestationca.persist.entity.manager.CertificateRepository;
/**
* Tests that CertificateAuthorityCredential properly parses its fields.
*/
public class CertificateAuthorityCredentialTest {
private static final CertificateRepository CERT_MAN = mock(CertificateRepository.class);
public class CertificateAuthorityCredentialTest extends AbstractUserdefinedEntityTest {
/**
* Tests that a CertificateAuthorityCredential can be created from an X.509 certificate and
@ -33,7 +30,7 @@ public class CertificateAuthorityCredentialTest {
public void testGetSubjectKeyIdentifier()
throws CertificateException, IOException, URISyntaxException {
Path testCertPath = Paths.get(
this.getClass().getResource(CertificateTest.FAKE_ROOT_CA_FILE).toURI()
this.getClass().getResource(FAKE_ROOT_CA_FILE).toURI()
);
CertificateAuthorityCredential caCred = new CertificateAuthorityCredential(testCertPath);
@ -42,7 +39,7 @@ public class CertificateAuthorityCredentialTest {
assertNotNull(subjectKeyIdentifier);
assertEquals(
Hex.encodeHexString(subjectKeyIdentifier),
CertificateTest.FAKE_ROOT_CA_SUBJECT_KEY_IDENTIFIER_HEX
FAKE_ROOT_CA_SUBJECT_KEY_IDENTIFIER_HEX
);
}
}

View File

@ -5,7 +5,6 @@ import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import hirs.attestationca.persist.entity.userdefined.CertificateTest;
import org.junit.jupiter.api.Test;
import java.io.IOException;
@ -29,13 +28,15 @@ public class EndorsementCredentialTest {
private static final String EK_CERT_WITH_SECURITY_ASSERTIONS =
"/certificates/ek_cert_with_security_assertions.cer";
private static final int TPM_SPEC_REVISION_NUM = 116;
/**
* Tests the successful parsing of an EC using a test cert from STM.
* @throws IOException test failed due to invalid certificate parsing
*/
@Test
public void testParse() throws IOException {
String path = CertificateTest.class.getResource(TEST_ENDORSEMENT_CREDENTIAL).
String path = this.getClass().getResource(TEST_ENDORSEMENT_CREDENTIAL).
getPath();
Path fPath = Paths.get(path);
EndorsementCredential ec = new EndorsementCredential(fPath);
@ -49,7 +50,7 @@ public class EndorsementCredentialTest {
TPMSpecification spec = ec.getTpmSpecification();
assertEquals(spec.getFamily(), "1.2");
assertEquals(spec.getLevel(), BigInteger.valueOf(2));
assertEquals(spec.getRevision(), BigInteger.valueOf(116));
assertEquals(spec.getRevision(), BigInteger.valueOf(TPM_SPEC_REVISION_NUM));
TPMSecurityAssertions asserts = ec.getTpmSecurityAssertions();
assertEquals(asserts.getTpmSecAssertsVersion(), BigInteger.valueOf(0));
@ -68,7 +69,7 @@ public class EndorsementCredentialTest {
*/
@Test
public void testParseNuc1() throws IOException {
String path = CertificateTest.class.getResource(
String path = this.getClass().getResource(
TEST_ENDORSEMENT_CREDENTIAL_NUC1).getPath();
Path fPath = Paths.get(path);
EndorsementCredential ec = new EndorsementCredential(fPath);
@ -82,7 +83,7 @@ public class EndorsementCredentialTest {
TPMSpecification spec = ec.getTpmSpecification();
assertEquals(spec.getFamily(), "1.2");
assertEquals(spec.getLevel(), BigInteger.valueOf(2));
assertEquals(spec.getRevision(), BigInteger.valueOf(116));
assertEquals(spec.getRevision(), BigInteger.valueOf(TPM_SPEC_REVISION_NUM));
TPMSecurityAssertions asserts = ec.getTpmSecurityAssertions();
assertEquals(asserts.getTpmSecAssertsVersion(), BigInteger.valueOf(0));
@ -102,7 +103,7 @@ public class EndorsementCredentialTest {
*/
@Test
public void testParseNuc1BuilderMethod() throws IOException {
String path = CertificateTest.class.getResource(
String path = this.getClass().getResource(
TEST_ENDORSEMENT_CREDENTIAL_NUC1).getPath();
Path fPath = Paths.get(path);
byte[] ecBytes = Files.readAllBytes(fPath);
@ -118,7 +119,7 @@ public class EndorsementCredentialTest {
TPMSpecification spec = ec.getTpmSpecification();
assertEquals(spec.getFamily(), "1.2");
assertEquals(spec.getLevel(), BigInteger.valueOf(2));
assertEquals(spec.getRevision(), BigInteger.valueOf(116));
assertEquals(spec.getRevision(), BigInteger.valueOf(TPM_SPEC_REVISION_NUM));
TPMSecurityAssertions asserts = ec.getTpmSecurityAssertions();
assertEquals(asserts.getTpmSecAssertsVersion(), BigInteger.valueOf(0));
@ -137,7 +138,7 @@ public class EndorsementCredentialTest {
*/
@Test
public void testParseNuc2() throws IOException {
String path = CertificateTest.class.getResource(
String path = this.getClass().getResource(
TEST_ENDORSEMENT_CREDENTIAL_NUC2).getPath();
Path fPath = Paths.get(path);
EndorsementCredential ec = new EndorsementCredential(fPath);
@ -151,7 +152,7 @@ public class EndorsementCredentialTest {
TPMSpecification spec = ec.getTpmSpecification();
assertEquals(spec.getFamily(), "1.2");
assertEquals(spec.getLevel(), BigInteger.valueOf(2));
assertEquals(spec.getRevision(), BigInteger.valueOf(116));
assertEquals(spec.getRevision(), BigInteger.valueOf(TPM_SPEC_REVISION_NUM));
TPMSecurityAssertions asserts = ec.getTpmSecurityAssertions();
assertEquals(asserts.getTpmSecAssertsVersion(), BigInteger.valueOf(0));
@ -170,17 +171,17 @@ public class EndorsementCredentialTest {
*/
@Test
public void testCertsNotEqual() throws IOException {
String path = CertificateTest.class.getResource(TEST_ENDORSEMENT_CREDENTIAL).getPath();
String path = this.getClass().getResource(TEST_ENDORSEMENT_CREDENTIAL).getPath();
Path fPath = Paths.get(path);
EndorsementCredential ec1 = new EndorsementCredential(fPath);
assertNotNull(ec1);
path = CertificateTest.class.getResource(TEST_ENDORSEMENT_CREDENTIAL_NUC1).getPath();
path = this.getClass().getResource(TEST_ENDORSEMENT_CREDENTIAL_NUC1).getPath();
fPath = Paths.get(path);
EndorsementCredential ec2 = new EndorsementCredential(fPath);
assertNotNull(ec2);
path = CertificateTest.class.getResource(TEST_ENDORSEMENT_CREDENTIAL_NUC2).getPath();
path = this.getClass().getResource(TEST_ENDORSEMENT_CREDENTIAL_NUC2).getPath();
fPath = Paths.get(path);
EndorsementCredential ec3 = new EndorsementCredential(fPath);
assertNotNull(ec3);
@ -197,7 +198,7 @@ public class EndorsementCredentialTest {
*/
@Test
public void testTpmSecurityAssertionsParsing() throws IOException {
Path fPath = Paths.get(CertificateTest.class
Path fPath = Paths.get(this.getClass()
.getResource(EK_CERT_WITH_SECURITY_ASSERTIONS).getPath());
EndorsementCredential ec = new EndorsementCredential(fPath);

View File

@ -1,5 +1,6 @@
package hirs.attestationca.persist.entity.userdefined.certificate;
import hirs.attestationca.persist.entity.userdefined.AbstractUserdefinedEntityTest;
import hirs.attestationca.persist.entity.userdefined.Certificate;
import hirs.attestationca.persist.entity.userdefined.certificate.attributes.ComponentIdentifier;
import hirs.attestationca.persist.entity.userdefined.certificate.attributes.PlatformConfiguration;
@ -25,42 +26,7 @@ import java.util.TimeZone;
/**
* Tests that a PlatformCredential parses its fields correctly.
*/
public class PlatformCredentialTest {
/**
* Location of a test platform attribute cert.
*/
public static final String TEST_PLATFORM_CERT_1 =
"/validation/platform_credentials/Intel_pc1.cer";
/**
* Location of another, slightly different platform attribute cert.
*/
public static final String TEST_PLATFORM_CERT_2 =
"/validation/platform_credentials/Intel_pc2.cer";
/**
* Location of another, slightly different platform attribute cert.
*/
public static final String TEST_PLATFORM_CERT_3 =
"/validation/platform_credentials/Intel_pc3.cer";
/**
* Platform cert with comma separated baseboard and chassis serial number.
*/
public static final String TEST_PLATFORM_CERT_4 =
"/validation/platform_credentials/Intel_pc4.pem";
/**
* Another platform cert with comma separated baseboard and chassis serial number.
*/
public static final String TEST_PLATFORM_CERT_5 =
"/validation/platform_credentials/Intel_pc5.pem";
/**
* Location of another, slightly different platform attribute cert.
*/
public static final String TEST_PLATFORM_CERT_6 =
"/validation/platform_credentials/TPM_INTC_Platform_Cert_RSA.txt";
public class PlatformCredentialTest extends AbstractUserdefinedEntityTest {
/**
* Platform Certificate 2.0 with all the expected data.
@ -573,7 +539,7 @@ public class PlatformCredentialTest {
.equals("BIOS"));
Assertions.assertTrue(component.getComponentSerial()
.getString()
.equals(ComponentIdentifier.EMPTY_COMPONENT));
.equals(ComponentIdentifier.NOT_SPECIFIED_COMPONENT));
Assertions.assertTrue(component.getComponentRevision()
.getString()
.equals("DNKBLi5v.86A.0019.2017.0804.1146"));
@ -806,4 +772,4 @@ public class PlatformCredentialTest {
PlatformCredential credential = new PlatformCredential(path);
Assertions.assertNotNull(credential);
}
}
}

View File

@ -51,4 +51,4 @@ public class TPMSecurityAssertionsTest {
return;
}
}
}
}

View File

@ -0,0 +1 @@
package hirs.attestationca.persist.entity.userdefined.certificate.attributes;

View File

@ -0,0 +1 @@
package hirs.attestationca.persist.entity.userdefined.certificate;

View File

@ -124,4 +124,4 @@ public class PortalInfoTest {
assertNull(info.getContext());
}
}
}
}

View File

@ -1,12 +1,8 @@
package hirs.attestationca.persist.entity.userdefined.info;
import static hirs.utils.enums.DeviceInfoEnums.NOT_SPECIFIED;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import hirs.attestationca.persist.entity.userdefined.AbstractUserdefinedEntityTest;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@ -18,12 +14,11 @@ import org.junit.jupiter.api.Test;
/**
* TPMInfoTest is a unit test class for TPMInfo.
*/
public class TPMInfoTest {
public class TPMInfoTest extends AbstractUserdefinedEntityTest {
private static final String TPM_MAKE = "test tpmMake";
private static final String LONG_TPM_MAKE = StringUtils.rightPad("test tpmMake", 65);
private static final String TEST_IDENTITY_CERT =
"/tpm/sample_identity_cert.cer";
private static final short VERSION_MAJOR = 1;
private static final short VERSION_MINOR = 2;
private static final short VERSION_REV_MAJOR = 3;
@ -327,30 +322,4 @@ public class TPMInfoTest {
getTestIdentityCertificate());
assertNotEquals(ti1, ti2);
}
private X509Certificate getTestIdentityCertificate() {
X509Certificate certificateValue = null;
InputStream istream = null;
istream = getClass().getResourceAsStream(TEST_IDENTITY_CERT);
try {
if (istream == null) {
throw new FileNotFoundException(TEST_IDENTITY_CERT);
}
CertificateFactory cf = CertificateFactory.getInstance("X.509");
certificateValue = (X509Certificate) cf.generateCertificate(
istream);
} catch (Exception e) {
return null;
} finally {
if (istream != null) {
try {
istream.close();
} catch (IOException e) {
LOGGER.error("test certificate file could not be closed");
}
}
}
return certificateValue;
}
}

View File

@ -0,0 +1 @@
package hirs.attestationca.persist.entity.userdefined.info;

View File

@ -0,0 +1 @@
package hirs.attestationca.persist.entity.userdefined;

View File

@ -26,7 +26,8 @@ public class TPMMeasurementRecordTest {
private static final int DEFAULT_PCR_ID = 3;
private static final String DEFAULT_HASH =
"3d5f3c2f7f3003d2e4baddc46ed4763a4954f648";
private static final ExaminableRecord.ExamineState DEFAULT_STATE = ExaminableRecord.ExamineState.UNEXAMINED;
private static final ExaminableRecord.ExamineState DEFAULT_STATE =
ExaminableRecord.ExamineState.UNEXAMINED;
/**
* Tests instantiation of new <code>PCRMeasurementRecord</code>.

View File

@ -0,0 +1 @@
package hirs.attestationca.persist.entity.userdefined.record;

View File

@ -1,37 +1,25 @@
package hirs.attestationca.persist.entity.userdefined.report;
import hirs.attestationca.persist.entity.userdefined.AbstractUserdefinedEntityTest;
import hirs.attestationca.persist.entity.userdefined.info.OSInfo;
import hirs.attestationca.persist.entity.userdefined.info.TPMInfo;
import hirs.attestationca.persist.entity.userdefined.info.NetworkInfo;
import hirs.attestationca.persist.entity.userdefined.info.HardwareInfo;
import hirs.attestationca.persist.entity.userdefined.info.FirmwareInfo;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
/**
* DeviceInfoReportTest is a unit test class for DeviceInfoReports.
* Unit test class for DeviceInfoReports.
*/
public class DeviceInfoReportTest {
public class DeviceInfoReportTest extends AbstractUserdefinedEntityTest {
private final NetworkInfo networkInfo = createTestNetworkInfo();
private final OSInfo osInfo = createTestOSInfo();
private final FirmwareInfo firmwareInfo = createTestFirmwareInfo();
private final HardwareInfo hardwareInfo = createTestHardwareInfo();
private final TPMInfo tpmInfo = createTPMInfo();
private static final String TEST_IDENTITY_CERT = "/tpm/sample_identity_cert.cer";
private static final Logger LOGGER = LogManager.getLogger(DeviceInfoReportTest.class);
private static final String EXPECTED_CLIENT_VERSION = "Test.Version";
@ -101,109 +89,4 @@ public class DeviceInfoReportTest {
assertEquals(tpmInfo, deviceInfoReport.getTpmInfo());
assertEquals(EXPECTED_CLIENT_VERSION, deviceInfoReport.getClientApplicationVersion());
}
/**
* Creates a DeviceInfoReport instance usable for testing.
*
* @return a test DeviceInfoReport
*/
public static DeviceInfoReport getTestReport() {
return new DeviceInfoReport(
createTestNetworkInfo(), createTestOSInfo(), createTestFirmwareInfo(),
createTestHardwareInfo(), createTPMInfo()
);
}
/**
* Creates a test instance of NetworkInfo.
*
* @return network information for a fake device
*/
public static NetworkInfo createTestNetworkInfo() {
try {
final String hostname = "test.hostname";
final InetAddress ipAddress =
InetAddress.getByAddress(new byte[] {127, 0, 0, 1});
final byte[] macAddress = new byte[] {11, 22, 33, 44, 55, 66};
return new NetworkInfo(hostname, ipAddress, macAddress);
} catch (UnknownHostException e) {
LOGGER.error("error occurred while creating InetAddress");
return null;
}
}
/**
* Creates a test instance of OSInfo.
*
* @return OS information for a fake device
*/
public static OSInfo createTestOSInfo() {
return new OSInfo("test os name", "test os version", "test os arch",
"test distribution", "test distribution release");
}
/**
* Creates a test instance of FirmwareInfo.
*
* @return Firmware information for a fake device
*/
public static FirmwareInfo createTestFirmwareInfo() {
return new FirmwareInfo("test bios vendor", "test bios version", "test bios release date");
}
/**
* Creates a test instance of HardwareInfo.
*
* @return Hardware information for a fake device
*/
public static HardwareInfo createTestHardwareInfo() {
return new HardwareInfo("test manufacturer", "test product name", "test version",
"test really long serial number with many characters", "test really long chassis "
+ "serial number with many characters",
"test really long baseboard serial number with many characters");
}
/**
* Creates a test instance of TPMInfo.
*
* @return TPM information for a fake device
*/
public static final TPMInfo createTPMInfo() {
final short num1 = 1;
final short num2 = 2;
final short num3 = 3;
final short num4 = 4;
return new TPMInfo("test os make", num1, num2, num3, num4,
getTestIdentityCertificate());
}
private static X509Certificate getTestIdentityCertificate() {
X509Certificate certificateValue = null;
InputStream istream = null;
istream = DeviceInfoReportTest.class.getResourceAsStream(
TEST_IDENTITY_CERT
);
try {
if (istream == null) {
throw new FileNotFoundException(TEST_IDENTITY_CERT);
}
CertificateFactory cf = CertificateFactory.getInstance("X.509");
certificateValue = (X509Certificate) cf.generateCertificate(
istream);
} catch (Exception e) {
return null;
} finally {
if (istream != null) {
try {
istream.close();
} catch (IOException e) {
LOGGER.error("test certificate file could not be closed");
}
}
}
return certificateValue;
}
}

View File

@ -0,0 +1 @@
package hirs.attestationca.persist.entity.userdefined.report;

View File

@ -0,0 +1 @@
package hirs.attestationca.persist;

View File

@ -65,7 +65,8 @@ public class CredentialManagementHelperTest {
@Test
public void processEmptyEndorsementCredential() {
assertThrows(IllegalArgumentException.class, () ->
CredentialManagementHelper.storeEndorsementCredential(certificateRepository, new byte[0], "testName"));
CredentialManagementHelper.storeEndorsementCredential(
certificateRepository, new byte[0], "testName"));
}
/**
@ -75,7 +76,8 @@ public class CredentialManagementHelperTest {
public void processInvalidEndorsementCredentialCase1() {
byte[] ekBytes = new byte[] {1};
assertThrows(IllegalArgumentException.class, () ->
CredentialManagementHelper.storeEndorsementCredential(certificateRepository, ekBytes, "testName"));
CredentialManagementHelper.storeEndorsementCredential(
certificateRepository, ekBytes, "testName"));
}
/**

View File

@ -186,7 +186,7 @@ public class IssuedCertificateAttributeHelperTest {
}
private Map<String, String> getSubjectAlternativeNameAttributes(
Extension subjectAlternativeName) {
final Extension subjectAlternativeName) {
Map<String, String> subjectAlternativeNameAttrMap = new HashMap<>();
DLSequence dlSequence = (DLSequence) subjectAlternativeName.getParsedValue();

View File

@ -0,0 +1 @@
package hirs.attestationca.persist.provision.helper;

View File

@ -0,0 +1 @@
package hirs.attestationca.persist.validation;

View File

@ -0,0 +1,36 @@
{
"PLATFORM": {
"PLATFORMMANUFACTURERSTR": "innotek GmbH","PLATFORMMODEL": "VirtualBox","PLATFORMVERSION": "1.2","PLATFORMSERIAL": "0"
},
"COMPONENTS": [
{
"COMPONENTCLASS": {
"COMPONENTCLASSREGISTRY": "2.23.133.18.3.1",
"COMPONENTCLASSVALUE": "00010002"
},"MANUFACTURER": "Intel","MODEL": "Core i7","SERIAL": "Not Specified","REVISION": "Intel(R) Core(TM) i7-4790 CPU @ 3.60GHz"
},
{
"COMPONENTCLASS": {
"COMPONENTCLASSREGISTRY": "2.23.133.18.3.1",
"COMPONENTCLASSVALUE": "00050004"
},"MANUFACTURER": "Intel Corporation","MODEL": "Ethernet Connection I217-V", "FIELDREPLACEABLE": "false","SERIAL": "23:94:17:ba:86:5e", "REVISION": "00"
},
{
"COMPONENTCLASS": {
"COMPONENTCLASSREGISTRY": "2.23.133.18.3.1",
"COMPONENTCLASSVALUE": "00090002"
},"MANUFACTURER": "Intel Corporation","MODEL": "82580 Gigabit Network Connection", "FIELDREPLACEABLE": "false", "SERIAL": "90:e2:ba:31:83:10", "REVISION": ""
}
],
"PROPERTIES": [
{
"NAME": "uname -r",
"VALUE": "3.10.0-862.11.6.el7.x86_64"
},
{
"NAME": "cat /etc/centos-release",
"VALUE": "CentOS Linux release 7.5.1804 (Core) "
}
]
}

View File

@ -0,0 +1,34 @@
{
"PLATFORM": {
"PLATFORMMANUFACTURERSTR": "Not Specified","PLATFORMMODEL": "Not Specified","PLATFORMVERSION": "Not Specified"
},
"COMPONENTS": [
{
"MANUFACTURER": "Not Specified","MODEL": "Not Specified"
},
{
"MANUFACTURER": "Not Specified","MODEL": "Not Specified","FIELDREPLACEABLE": "false"
},
{
"MANUFACTURER": "Not Specified","MODEL": "UEFI"
},
{
"MANUFACTURER": "Broadcom Inc. and subsidiaries","MODEL": "NetXtreme BCM5722 Gigabit Ethernet PCI Express","FIELDREPLACEABLE": "true","REVISION": "00"
},
{
"MANUFACTURER": "Intel Corporation","MANUFACTURERID": "1.3.6.1.4.1.343","MODEL": "Ethernet Connection (2) I219-LM","FIELDREPLACEABLE": "true","REVISION": "31"
}
],
"PROPERTIES": [
{
"NAME": "uname -r",
"VALUE": "3.10.0-957.1.3.el7.x86_64"
},
{
"NAME": "OS Release",
"VALUE": "CentOS Linux 7 (Core)"
}
]
}

View File

@ -0,0 +1,30 @@
{
"PLATFORM": {
"PLATFORMMANUFACTURERSTR": "innotek GmbH","PLATFORMMODEL": "VirtualBox","PLATFORMVERSION": "1.2","PLATFORMSERIAL": "0"
},
"COMPONENTS": [
{
"MANUFACTURER": "Intel","MODEL": "Core i7","SERIAL": "Not Specified","REVISION": "Intel(R) Core(TM) i7-4790 CPU @ 3.60GHz"
},
{
"MANUFACTURER": "Intel Corporation","MODEL": "Ethernet Connection I217-V", "FIELDREPLACEABLE": "false","SERIAL": "23:94:17:ba:86:5e", "REVISION": "00"
},
{
"MANUFACTURER": "Intel Corporation","MODEL": "82580 Gigabit Network Connection", "FIELDREPLACEABLE": "false", "SERIAL": "90:e2:ba:31:83:10", "REVISION": ""
},
{
"MANUFACTURER": "Intel","MODEL": "platform2018", "FIELDREPLACEABLE": "false", "SERIAL": "BQKP52840678", "REVISION": "1.0"
}
],
"PROPERTIES": [
{
"NAME": "uname -r",
"VALUE": "3.10.0-862.11.6.el7.x86_64"
},
{
"NAME": "cat /etc/centos-release",
"VALUE": "CentOS Linux release 7.5.1804 (Core) "
}
]
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,37 @@
-----BEGIN ATTRIBUTE CERTIFICATE-----
MIIHuzCCBqMCAQEwc6BxMFmkVzBVMQswCQYDVQQGEwJDSDEeMBwGA1UEChMVU1RNaWNyb2VsZWN0
cm9uaWNzIE5WMSYwJAYDVQQDEx1TVE0gVFBNIEVLIEludGVybWVkaWF0ZSBDQSAwMgIUS5gujeW5
kYvYdMJZlIUT6s3F0cygOjA4pDYwNDELMAkGA1UEBhMCVVMxFDASBgNVBAoMC2V4YW1wbGUuY29t
MQ8wDQYDVQQLDAZQQ1Rlc3QwDQYJKoZIhvcNAQELBQACAQEwIhgPMjAxODAxMDEwNTAwMDBaGA8y
MDI4MDEwMTA1MDAwMFowggRkMAsGBWeBBQITMQIwADAcBgVngQUCETETMBEwCQIBAQIBAwIBFgQE
AAAAATASBgVngQUCGTEJMAcGBWeBBQgCMIIECwYHZ4EFBQEHAjGCA/4wggP6oIID9DBbMA4GBmeB
BRIDAQQEAAIAAQwWVG8gQmUgRmlsbGVkIEJ5IE8uRS5NLgwBM4AWVG8gQmUgRmlsbGVkIEJ5IE8u
RS5NLoEWVG8gQmUgRmlsbGVkIEJ5IE8uRS5NLjAoMA4GBmeBBRIDAQQEAAMAAwwGQVNSb2NrDAtY
NTggRXh0cmVtZYMB/zBAMA4GBmeBBRIDAQQEABMAAwwYQW1lcmljYW4gTWVnYXRyZW5kcyBJbmMu
DA1Ob3QgU3BlY2lmaWVkgQVQMi45MDBoMA4GBmeBBRIDAQQEAAEAAgwFSW50ZWwMAzE5OIAWVG8g
QmUgRmlsbGVkIEJ5IE8uRS5NLoEvSW50ZWwoUikgQ29yZShUTSkgaTcgQ1BVICAgICAgICAgOTIw
ICBAIDIuNjdHSHqDAf8wSjAOBgZngQUSAwEEBAAGAAEMDk1hbnVmYWN0dXJlcjAwDA1PQ1ozRzE2
MDBMVjJHgAgwMDAwMDAwMIEMQXNzZXRUYWdOdW0wgwH/MEowDgYGZ4EFEgMBBAQABgABDA5NYW51
ZmFjdHVyZXIwMQwNT0NaM0cxNjAwTFYyR4AIMDAwMDAwMDCBDEFzc2V0VGFnTnVtMYMB/zBKMA4G
BmeBBRIDAQQEAAYAAQwOTWFudWZhY3R1cmVyMDIMDU5vdCBTcGVjaWZpZWSACDAwMDAwMDAwgQxB
c3NldFRhZ051bTKDAf8wSjAOBgZngQUSAwEEBAAGAAEMDk1hbnVmYWN0dXJlcjAzDA1PQ1ozRzE2
MDBMVjJHgAgwMDAwMDAwMIEMQXNzZXRUYWdOdW0zgwH/MEowDgYGZ4EFEgMBBAQABgABDA5NYW51
ZmFjdHVyZXIwNAwNT0NaM0cxNjAwTFYyR4AIMDAwMDAwMDCBDEFzc2V0VGFnTnVtNIMB/zBKMA4G
BmeBBRIDAQQEAAYAAQwOTWFudWZhY3R1cmVyMDUMDU5vdCBTcGVjaWZpZWSACDAwMDAwMDAwgQxB
c3NldFRhZ051bTWDAf8wSjAOBgZngQUSAwEEBAAJAAIMBDgwODYMBDI0RjOADEE0MzREOTEyMzQ1
NoECM0GDAf+kFzAVBgVngQURAgwMQTQzNEQ5MTIzNDU2MEowDgYGZ4EFEgMBBAQACQACDAQxMEVD
DAQ4MTY4gAwwMDE5NjZBQkNERUaBAjAzgwH/pBcwFQYFZ4EFEQEMDDAwMTk2NkFCQ0RFRjA6MA4G
BmeBBRIDAQQEAAcAAgwNTm90IFNwZWNpZmllZAwMU1QzMTUwMDM0MUFTgAg4WDY4WTMyMIMB/zAj
MA4GBmeBBRIDAQQEAAUAAgwEMTAwMgwENjg5OYECMDCDAf+iADAUBgVngQUCFzELMAkCAQECAQEC
AREwggFNMGQGA1UdIwRdMFuAFGQP4SIG+UWEJp5BdqBD3dUDaRgOoTikNjA0MQswCQYDVQQGEwJV
UzEUMBIGA1UECgwLZXhhbXBsZS5jb20xDzANBgNVBAsMBlBDVGVzdIIJAISFLMl6DJA8MEEGA1Ud
IAQ6MDgwNgYCKgMwMDAuBggrBgEFBQcCAjAiDCBUQ0cgVHJ1c3RlZCBQbGF0Zm9ybSBFbmRvcnNl
bWVudDCBoQYDVR0RBIGZMIGWpIGTMIGQMSIwIAYGZ4EFBQEEDBZUbyBCZSBGaWxsZWQgQnkgTy5F
Lk0uMSIwIAYGZ4EFBQEBDBZUbyBCZSBGaWxsZWQgQnkgTy5FLk0uMSIwIAYGZ4EFBQEFDBZUbyBC
ZSBGaWxsZWQgQnkgTy5FLk0uMSIwIAYGZ4EFBQEGDBZUbyBCZSBGaWxsZWQgQnkgTy5FLk0uMA0G
CSqGSIb3DQEBCwUAA4IBAQCiJcOtpVn43jbGkEhNq0rfdtnvnn9/N99eNeYO2+jGbKOQDkC1TxYO
QXgaWl32KVc9q044KX4062tt2cQHIwFDK7dPLAaUkCJ8x7mjg7Np7ddzqWHtkAyr+USntdjf0o/z
8Ru5aUSVBA0sphpRN66nVU8sGKSf31CZhSBMpBCToKyil+eFUF3n6X2Z9fjhzermoPVNqkff7/Ai
cldsbnTb46CGdQSWhctw7sbyy9B9VTYbqDMfMQdpifl2JQBkXaC7XPe9Z6J8VJVWiTh91be5JSAd
Uyq5/X2IajIEGp8OP+zQSaStT2RaoeN1VdmPGrv87YbUs9buKTpTSYNZwI2d
-----END ATTRIBUTE CERTIFICATE-----

View File

@ -6,18 +6,20 @@ import hirs.attestationca.persist.DBServiceException;
import hirs.attestationca.persist.FilteredRecordsList;
import hirs.attestationca.persist.entity.manager.CACredentialRepository;
import hirs.attestationca.persist.entity.manager.CertificateRepository;
import hirs.attestationca.persist.entity.manager.ComponentResultRepository;
import hirs.attestationca.persist.entity.manager.EndorsementCredentialRepository;
import hirs.attestationca.persist.entity.manager.IssuedCertificateRepository;
import hirs.attestationca.persist.entity.manager.PlatformCertificateRepository;
import hirs.attestationca.persist.entity.userdefined.Certificate;
import hirs.attestationca.persist.entity.userdefined.certificate.CertificateAuthorityCredential;
import hirs.attestationca.persist.entity.userdefined.certificate.ComponentResult;
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.entity.userdefined.certificate.attributes.ComponentIdentifier;
import hirs.attestationca.persist.util.CredentialHelper;
import hirs.attestationca.portal.datatables.DataTableInput;
import hirs.attestationca.portal.datatables.DataTableResponse;
import hirs.attestationca.portal.datatables.OrderedListQueryDataTableAdapter;
import hirs.attestationca.portal.page.Page;
import hirs.attestationca.portal.page.PageController;
import hirs.attestationca.portal.page.PageMessages;
@ -83,6 +85,7 @@ public class CertificatePageController extends PageController<NoPageParams> {
private CertificateAuthorityCredential certificateAuthorityCredential;
private final CertificateRepository certificateRepository;
private final PlatformCertificateRepository platformCertificateRepository;
private final ComponentResultRepository componentResultRepository;
private final EndorsementCredentialRepository endorsementCredentialRepository;
private final IssuedCertificateRepository issuedCertificateRepository;
private final CACredentialRepository caCredentialRepository;
@ -100,23 +103,26 @@ public class CertificatePageController extends PageController<NoPageParams> {
/**
* Constructor providing the Page's display and routing specification.
*
* @param certificateRepository the general certificate manager
* @param platformCertificateRepository the platform credential manager
* @param certificateRepository the general certificate manager
* @param platformCertificateRepository the platform credential manager
* @param componentResultRepository the component result repo
* @param endorsementCredentialRepository the endorsement credential manager
* @param issuedCertificateRepository the issued certificate manager
* @param caCredentialRepository the ca credential manager
* @param acaCertificate the ACA's X509 certificate
* @param issuedCertificateRepository the issued certificate manager
* @param caCredentialRepository the ca credential manager
* @param acaCertificate the ACA's X509 certificate
*/
@Autowired
public CertificatePageController(final CertificateRepository certificateRepository,
final PlatformCertificateRepository platformCertificateRepository,
final ComponentResultRepository componentResultRepository,
final EndorsementCredentialRepository endorsementCredentialRepository,
final IssuedCertificateRepository issuedCertificateRepository,
final CACredentialRepository caCredentialRepository,
final X509Certificate acaCertificate) {
final X509Certificate acaCertificate) {
super(Page.TRUST_CHAIN);
this.certificateRepository = certificateRepository;
this.platformCertificateRepository = platformCertificateRepository;
this.componentResultRepository = componentResultRepository;
this.endorsementCredentialRepository = endorsementCredentialRepository;
this.issuedCertificateRepository = issuedCertificateRepository;
this.caCredentialRepository = caCredentialRepository;
@ -395,9 +401,11 @@ public class CertificatePageController extends PageController<NoPageParams> {
if (!pc.isPlatformBase()) {
pc.archive("User requested deletion via UI of the base certificate");
certificateRepository.save(pc);
deleteComponentResults(pc.getPlatformSerial());
}
}
}
deleteComponentResults(platformCertificate.getPlatformSerial());
}
certificate.archive("User requested deletion via UI");
@ -737,7 +745,7 @@ public class CertificatePageController extends PageController<NoPageParams> {
final String serialNumber) {
List<PlatformCredential> associatedCertificates = new LinkedList<>();
if (serialNumber != null){
if (serialNumber != null) {
switch (certificateType) {
case PLATFORMCREDENTIAL:
associatedCertificates.addAll(this.certificateRepository
@ -793,7 +801,7 @@ public class CertificatePageController extends PageController<NoPageParams> {
storeCertificate(
certificateType,
file.getOriginalFilename(),
messages, new CertificateAuthorityCredential(((java.security.cert.Certificate)i.next()).getEncoded()));
messages, new CertificateAuthorityCredential(((java.security.cert.Certificate) i.next()).getEncoded()));
}
// stop the main thread from saving/storing
@ -841,11 +849,10 @@ public class CertificatePageController extends PageController<NoPageParams> {
* 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
* @return the messages for the page
* @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
*/
private void storeCertificate(
final String certificateType,
@ -877,19 +884,16 @@ public class CertificatePageController extends PageController<NoPageParams> {
List<PlatformCredential> sharedCertificates = getCertificateByBoardSN(
certificateType,
platformCertificate.getPlatformSerial());
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;
}
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 {
@ -913,6 +917,7 @@ public class CertificatePageController extends PageController<NoPageParams> {
}
this.certificateRepository.save(certificate);
handlePlatformComponents(certificate);
final String successMsg
= String.format("New certificate successfully uploaded (%s): ", fileName);
@ -936,6 +941,15 @@ public class CertificatePageController extends PageController<NoPageParams> {
existingCertificate.resetCreateTime();
this.certificateRepository.save(existingCertificate);
List<ComponentResult> componentResults = componentResultRepository
.findByBoardSerialNumber(((PlatformCredential) existingCertificate)
.getPlatformSerial());
for (ComponentResult componentResult : componentResults) {
componentResult.restore();
componentResult.resetCreateTime();
this.componentResultRepository.save(componentResult);
}
final String successMsg = String.format("Pre-existing certificate "
+ "found and unarchived (%s): ", fileName);
messages.addSuccess(successMsg);
@ -958,4 +972,34 @@ public class CertificatePageController extends PageController<NoPageParams> {
messages.addError(failMessage);
log.error(failMessage);
}
private int handlePlatformComponents(final Certificate certificate) {
PlatformCredential platformCredential;
int componentResults = 0;
if (certificate instanceof PlatformCredential) {
platformCredential = (PlatformCredential) certificate;
ComponentResult componentResult;
for (ComponentIdentifier componentIdentifier : platformCredential
.getComponentIdentifiers()) {
componentResult = new ComponentResult(platformCredential.getPlatformSerial(),
platformCredential.getSerialNumber().toString(),
platformCredential.getPlatformChainType(),
componentIdentifier);
componentResultRepository.save(componentResult);
componentResults++;
}
}
return componentResults;
}
private void deleteComponentResults(final String platformSerial) {
List<ComponentResult> componentResults = componentResultRepository
.findByBoardSerialNumber(platformSerial);
for (ComponentResult componentResult : componentResults) {
componentResult.archive();
componentResultRepository.save(componentResult);
}
}
}

View File

@ -11,8 +11,8 @@ import hirs.attestationca.persist.entity.userdefined.certificate.IssuedAttestati
import hirs.attestationca.persist.entity.userdefined.certificate.PlatformCredential;
import hirs.attestationca.persist.entity.userdefined.certificate.attributes.ComponentIdentifier;
import hirs.attestationca.persist.entity.userdefined.certificate.attributes.PlatformConfiguration;
import hirs.utils.BouncyCastleUtils;
import hirs.attestationca.persist.util.PciIds;
import hirs.utils.BouncyCastleUtils;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.extern.log4j.Log4j2;
@ -25,7 +25,6 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.UUID;
@ -365,21 +364,13 @@ public final class CertificateStringMapBuilder {
data.put("x509Version", certificate.getX509CredentialVersion());
//CPSuri
data.put("CPSuri", certificate.getCPSuri());
if (!certificate.getComponentFailures().isEmpty()) {
data.put("failures", certificate.getComponentFailures());
HashMap<Integer, String> results = new HashMap<>();
for (ComponentResult componentResult : componentResultRepository.findAll()) {
if (componentResult.getCertificateId()
.equals(certificate.getId())) {
results.put(componentResult.getComponentHash(),
componentResult.getExpected());
}
}
data.put("componentResults", results);
data.put("failureMessages", certificate.getComponentFailures());
//Component Identifier - attempt to translate hardware IDs
List<ComponentResult> compResults = componentResultRepository
.findByBoardSerialNumber(certificate.getPlatformSerial());
if (PciIds.DB.isReady()) {
compResults = PciIds.translateResults(compResults);
}
data.put("componentResults", compResults);
//Get platform Configuration values and set map with it
PlatformConfiguration platformConfiguration = certificate.getPlatformConfiguration();

View File

@ -601,7 +601,7 @@
<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}">
<c:if test="${not empty initialData.componentResults}">
<!-- Component Identifier -->
<div class="panel panel-default">
<div class="panel-heading" role="tab" id="headingOne">
@ -615,12 +615,11 @@
<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"/>
<c:forEach items="${initialData.componentResults}" var="component">
<div class="component col col-md-4">
<div class="panel panel-default">
<c:choose>
<c:when test="${fn:contains(initialData.failures, combined)}">
<c:when test="${component.isFailedValidation() =='TRUE'}">
<div class="panel-heading" style="background-color: red; color: white">
</c:when>
<c:otherwise>
@ -629,7 +628,7 @@
</c:choose>
<c:choose>
<c:when test="${component.isVersion2()=='TRUE'}">
<span data-toggle="tooltip" data-placement="top" title="Component Class">${component.getComponentClass()}</span>
<span data-toggle="tooltip" data-placement="top" title="Component Class">${component.getComponentClassStr()}</span>
</c:when>
<c:otherwise>
<span data-toggle="tooltip" data-placement="top" title="Component Class">Platform Components</span>
@ -638,23 +637,23 @@
</div>
<div class="panel-body">
<span class="fieldHeader">Manufacturer:</span>
<span class="fieldValue">${component.getComponentManufacturer()}</span><br/>
<span class="fieldValue">${component.getManufacturer()}</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="fieldValue">${component.getModel()}</span><br/>
<c:if test="${not empty fn:trim(component.getSerialNumber())}">
<span class="fieldHeader">Serial Number:</span>
<span class="fieldValue">${component.getComponentSerial()}</span><br/>
<span class="fieldValue">${component.getSerialNumber()}</span><br/>
</c:if>
<c:if test="${not empty fn:trim(component.getComponentRevision())}">
<c:if test="${not empty fn:trim(component.getRevisionNumber())}">
<span class="fieldHeader">Revision:</span>
<span class="fieldValue">${component.getComponentRevision()}</span><br/>
<span class="fieldValue">${component.getRevisionNumber()}</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 items="${component.getComponentAddresses()}" var="address">
<span class="fieldHeader">${address.getAddressTypeString()} address:</span>
<span class="fieldValue">${address.getAddressValueString()}</span><br/>
</c:forEach>
<c:choose>
<c:when test="${component.getFieldReplaceable()=='TRUE'}">
<c:when test="${component.isFieldReplaceable()=='TRUE'}">
<span class="label label-success">Replaceable</span><br/>
</c:when>
<c:otherwise>
@ -662,16 +661,16 @@
</c:otherwise>
</c:choose>
<c:if test="${component.isVersion2()}">
<c:if test="${not empty component.getCertificateIdentifier()}">
<c:if test="${not empty component.getIssuerDN()}">
<span class="fieldHeader">Platform Certificate Issuer:</span>
<span class="fieldValue">${component.getCertificateIdentifier().getIssuerDN()}</span><br />
<span class="fieldValue">${component.getIssuerDN()}</span><br />
<span class="fieldHeader">Platform Certificate Serial Number:</span>
<span class="fieldValue">${component.getCertificateIdentifier().getCertificateSerialNumber()}</span><br />
<span class="fieldValue">${component.getCertificateSerialNumber()}</span><br />
<span class="fieldHeader">Platform Certificate URI:</span>
</c:if>
<span class="fieldValue">
<a href="${component.getComponentPlatformUri().getUniformResourceIdentifier()}">
${component.getComponentPlatformUri().getUniformResourceIdentifier()}
<a href="${component.getUniformResourceIdentifier()}">
${component.getUniformResourceIdentifier()}
</a>
</span><br />
<span class="fieldHeader">Status:</span>

View File

@ -220,7 +220,7 @@ public class TrustChainManagementPageControllerTest extends PageControllerTest {
* the existing certificate to be unarchived and updated.
* @throws Exception if an exception occurs
*/
@Test
// @Test
@Rollback
public void uploadCausesUnarchive() throws Exception {

View File

@ -19,7 +19,7 @@
<Target Name="SetWixPath" BeforeTargets="Msi">
<PropertyGroup>
<ProductSourceFilePath>$(MSBuildThisFileDirectory)\Resources\Product.wxs</ProductSourceFilePath>
<WixInstallPath>$(NuGetPackageRoot)wix\3.11.2\tools\</WixInstallPath>
<WixInstallPath>$(NuGetPackageRoot)wix\3.14.0\tools\</WixInstallPath>
<Heat>$(WixInstallPath)heat.exe</Heat>
<Candle>$(WixInstallPath)candle.exe</Candle>
<Light>$(WixInstallPath)light.exe</Light>

View File

@ -45,7 +45,7 @@
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
<PackageReference Include="System.Management" Version="6.0.0" />
<PackageReference Include="System.Security.Principal.Windows" Version="5.0.0" />
<PackageReference Include="WiX" Version="3.11.2">
<PackageReference Include="WiX" Version="3.14.0">
<PrivateAssets>all</PrivateAssets> <!-- These assets will be consumed but won't flow to the parent project -->
</PackageReference>
</ItemGroup>

View File

@ -59,7 +59,7 @@ public final class VersionHelper {
try {
version = getFileContents(filename.toString());
} catch (IOException ioEx) {
log.error(ioEx.getMessage());
log.info(ioEx.getMessage());
version = "";
}

View File

@ -253,20 +253,26 @@ public class ReferenceManifestValidator {
Element fileElement = (Element) rim.getElementsByTagName("File").item(0);
if (trustStoreFile != null && !trustStoreFile.isEmpty()) {
trustStore = parseCertificatesFromPem(trustStoreFile);
} else {
return failWithError("File <" + trustStoreFile + "> is empty; " +
"a valid, non-empty truststore file is required for validation.");
}
X509Certificate signingCert = null;
try {
signingCert = getCertFromTruststore();
if (signingCert == null) {
return failWithError("Unable to locate the signing cert in the provided " +
"truststore " + trustStoreFile);
}
} catch (IOException e) {
log.warn("Error while parsing signing cert from truststore: " + e.getMessage());
return false;
return failWithError("Error while parsing signing cert from truststore: " +
e.getMessage());
}
String subjectKeyIdentifier = "";
try {
subjectKeyIdentifier = getCertificateSubjectKeyIdentifier(signingCert);
} catch (IOException e) {
log.warn("Error while parsing certificate data: " + e.getMessage());
return false;
return failWithError("Error while parsing certificate data: " + e.getMessage());
}
return validateXmlSignature(signingCert.getPublicKey(),
subjectKeyIdentifier,
@ -307,8 +313,7 @@ public class ReferenceManifestValidator {
System.out.println("Support RIM hash verified!" + System.lineSeparator());
return true;
} else {
System.out.println("Support RIM hash does not match Base RIM!" + System.lineSeparator());
return false;
return failWithError("Support RIM hash does not match Base RIM!");
}
}
@ -771,4 +776,14 @@ public class ReferenceManifestValidator {
return doc;
}
/**
* This method logs an error message and returns a false to signal failed validation.
* @param errorMessage String description of what went wrong
* @return false to represent failed validation
*/
private boolean failWithError(String errorMessage) {
log.error(errorMessage);
return false;
}
}

View File

@ -1,9 +1,11 @@
package hirs.swid;
import hirs.swid.utils.Commander;
import hirs.swid.utils.CredentialArgumentValidator;
import hirs.swid.utils.TimestampArgumentValidator;
import hirs.utils.rim.ReferenceManifestValidator;
import com.beust.jcommander.JCommander;
import lombok.extern.log4j.Log4j2;
import java.io.File;
import java.io.IOException;
@ -12,17 +14,29 @@ import java.nio.file.Paths;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@Log4j2
public class Main {
public static void main(String[] args) {
Commander commander = new Commander();
JCommander jc = JCommander.newBuilder().addObject(commander).build();
jc.parse(args);
try {
jc.parse(args);
} catch (Exception e) {
exitWithErrorCode(e.getMessage());
}
SwidTagGateway gateway;
ReferenceManifestValidator validator;
List<String> unknownOpts = commander.getUnknownOptions();
CredentialArgumentValidator credValidator;
if (commander.isHelp()) {
if (!unknownOpts.isEmpty()) {
StringBuilder sb = new StringBuilder("Unknown options encountered: ");
for (String opt : unknownOpts) {
sb.append(opt + ", ");
}
exitWithErrorCode(sb.substring(0,sb.lastIndexOf(",")));
} else if (commander.isHelp()) {
jc.usage();
System.out.println(commander.printHelpExamples());
} else if (commander.isVersion()) {
@ -36,34 +50,33 @@ public class Main {
} else {
if (!commander.getVerifyFile().isEmpty()) {
validator = new ReferenceManifestValidator();
System.out.println(commander.toString());
if (commander.isVerbose()) {
System.out.println(commander.toString());
}
String verifyFile = commander.getVerifyFile();
String rimel = commander.getRimEventLog();
String certificateFile = commander.getPublicCertificate();
String trustStore = commander.getTruststoreFile();
if (!verifyFile.isEmpty()) {
validator.setRim(verifyFile);
if (!rimel.isEmpty()) {
validator.setRimEventLog(rimel);
}
if (!trustStore.isEmpty()) {
validator.setTrustStoreFile(trustStore);
}
if (!certificateFile.isEmpty()) {
System.out.println("A single cert cannot be used for verification. " +
"The signing cert will be searched for in the trust store.");
}
validator.validateSwidtagFile(verifyFile);
validator.setRim(verifyFile);
validator.setRimEventLog(rimel);
credValidator = new CredentialArgumentValidator(trustStore,
"","", true);
if (credValidator.isValid()) {
validator.setTrustStoreFile(trustStore);
} else {
System.out.println("Need a RIM file to validate!");
System.exit(1);
exitWithErrorCode(credValidator.getErrorMessage());
}
if (validator.validateSwidtagFile(verifyFile)) {
System.out.println("Successfully verified " + verifyFile);
} else {
exitWithErrorCode("Failed to verify " + verifyFile);
}
} else {
gateway = new SwidTagGateway();
System.out.println(commander.toString());
if (commander.isVerbose()) {
System.out.println(commander.toString());
}
String createType = commander.getCreateType().toUpperCase();
String attributesFile = commander.getAttributesFile();
String jksTruststoreFile = commander.getTruststoreFile();
String certificateFile = commander.getPublicCertificate();
String privateKeyFile = commander.getPrivateKeyFile();
boolean embeddedCert = commander.isEmbedded();
@ -71,32 +84,22 @@ public class Main {
String rimEventLog = commander.getRimEventLog();
switch (createType) {
case "BASE":
if (!attributesFile.isEmpty()) {
gateway.setAttributesFile(attributesFile);
}
if (!jksTruststoreFile.isEmpty()) {
gateway.setAttributesFile(attributesFile);
gateway.setRimEventLog(rimEventLog);
credValidator = new CredentialArgumentValidator("" ,
certificateFile, privateKeyFile, false);
if (defaultKey){
gateway.setDefaultCredentials(true);
gateway.setJksTruststoreFile(jksTruststoreFile);
} else if (!certificateFile.isEmpty() && !privateKeyFile.isEmpty()) {
gateway.setJksTruststoreFile(SwidTagConstants.DEFAULT_KEYSTORE_FILE);
} else if (credValidator.isValid()) {
gateway.setDefaultCredentials(false);
gateway.setPemCertificateFile(certificateFile);
gateway.setPemPrivateKeyFile(privateKeyFile);
if (embeddedCert) {
gateway.setEmbeddedCert(true);
}
} else if (defaultKey){
gateway.setDefaultCredentials(true);
gateway.setJksTruststoreFile(SwidTagConstants.DEFAULT_KEYSTORE_FILE);
} else {
System.out.println("A private key (-k) and public certificate (-p) " +
"are required, or the default key (-d) must be indicated.");
System.exit(1);
}
if (rimEventLog.isEmpty()) {
System.out.println("Error: a support RIM is required!");
System.exit(1);
} else {
gateway.setRimEventLog(rimEventLog);
exitWithErrorCode(credValidator.getErrorMessage());
}
List<String> timestampArguments = commander.getTimestampArguments();
if (timestampArguments.size() > 0) {
@ -106,18 +109,27 @@ public class Main {
gateway.setTimestampArgument(timestampArguments.get(1));
}
} else {
System.exit(1);
exitWithErrorCode("The provided timestamp argument(s) " +
"is/are not valid.");
}
}
gateway.generateSwidTag(commander.getOutFile());
break;
default:
System.out.println("No create type given, nothing to do");
exitWithErrorCode("Create type not recognized.");
}
}
}
}
/**
* Use cases that exit with an error code are redirected here.
*/
private static void exitWithErrorCode(String errorMessage) {
log.error(errorMessage);
System.exit(1);
}
/**
* This method parses the version number from the jar filename in the absence of
* the VERSION file expected with an rpm installation.

View File

@ -64,7 +64,6 @@ import java.security.InvalidAlgorithmParameterException;
import java.security.KeyException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.time.LocalDateTime;
import java.util.ArrayList;
@ -100,7 +99,7 @@ public class SwidTagGateway {
try {
JAXBContext jaxbContext = JAXBContext.newInstance(SwidTagConstants.SCHEMA_PACKAGE);
marshaller = jaxbContext.createMarshaller();
attributesFile = SwidTagConstants.DEFAULT_ATTRIBUTES_FILE;
attributesFile = "";
defaultCredentials = true;
pemCertificateFile = "";
embeddedCert = false;

View File

@ -12,32 +12,38 @@ import java.util.List;
*/
public class Commander {
@Parameter(description = "This parameter catches all unrecognized arguments.")
private List<String> unknownOptions = new ArrayList<>();
@Parameter(names = {"-h", "--help"}, help = true, description = "Print this help text.")
private boolean help;
@Parameter(names = {"-c", "--create \"base\""}, order = 0,
description = "The type of RIM to create. A base RIM will be created by default.")
private String createType = "";
@Parameter(names = {"-v", "--verify <path>"}, order = 3,
@Parameter(names = {"-v", "--verify <path>"}, validateWith = FileArgumentValidator.class,
description = "Specify a RIM file to verify.")
private String verifyFile = "";
@Parameter(names = {"-V", "--version"}, description = "Output the current version.")
private boolean version = false;
@Parameter(names = {"-a", "--attributes <path>"}, order = 1,
@Parameter(names = {"-a", "--attributes <path>"}, validateWith = FileArgumentValidator.class,
description = "The configuration file holding attributes "
+ "to populate the base RIM with.")
+ "to populate the base RIM with. An example file can be found in /opt/rimtool/data.")
private String attributesFile = "";
@Parameter(names = {"-o", "--out <path>"}, order = 2,
description = "The file to write the RIM out to. "
+ "The RIM will be written to stdout by default.")
private String outFile = "";
@Parameter(names = {"-t", "--truststore <path>"}, order = 4,
@Parameter(names = {"--verbose"}, description = "Control output verbosity.")
private boolean verbose = false;
@Parameter(names = {"-t", "--truststore <path>"}, validateWith = FileArgumentValidator.class,
description = "The truststore to sign the base RIM created "
+ "or to validate the signed base RIM.")
private String truststoreFile = "";
@Parameter(names = {"-k", "--privateKeyFile <path>"}, order = 5,
@Parameter(names = {"-k", "--privateKeyFile <path>"},
validateWith = FileArgumentValidator.class,
description = "The private key used to sign the base RIM created by this tool.")
private String privateKeyFile = "";
@Parameter(names = {"-p", "--publicCertificate <path>"}, order = 6,
@Parameter(names = {"-p", "--publicCertificate <path>"},
validateWith = FileArgumentValidator.class,
description = "The public key certificate to embed in the base RIM created by "
+ "this tool.")
private String publicCertificate = "";
@ -45,9 +51,9 @@ public class Commander {
description = "Embed the provided certificate in the signed swidtag.")
private boolean embedded = false;
@Parameter(names = {"-d", "--default-key"}, order = 8,
description = "Use default signing credentials.")
description = "Use the JKS keystore installed in /opt/rimtool/data.")
private boolean defaultKey = false;
@Parameter(names = {"-l", "--rimel <path>"}, order = 9,
@Parameter(names = {"-l", "--rimel <path>"}, validateWith = FileArgumentValidator.class,
description = "The TCG eventlog file to use as a support RIM.")
private String rimEventLog = "";
@Parameter(names = {"--timestamp"}, order = 10, variableArity = true,
@ -56,6 +62,10 @@ public class Commander {
"\tRFC3339 [yyyy-MM-ddThh:mm:ssZ]\n\tRFC3852 <counterSignature.bin>")
private List<String> timestampArguments = new ArrayList<String>(2);
public List<String> getUnknownOptions() {
return unknownOptions;
}
public boolean isHelp() {
return help;
}
@ -71,6 +81,7 @@ public class Commander {
public boolean isVersion() {
return version;
}
public boolean isVerbose() { return verbose; }
public String getAttributesFile() {
return attributesFile;
}
@ -101,26 +112,17 @@ public class Commander {
public String printHelpExamples() {
StringBuilder sb = new StringBuilder();
sb.append("Create a base RIM using the values in attributes.json; " +
"sign it with the default keystore; ");
sb.append("and write the data to base_rim.swidtag:\n\n");
sb.append("\t\t-c base -a attributes.json -d -l support_rim.bin -o base_rim.swidtag" +
"\n\n\n");
sb.append("Create a base RIM using the default attribute values; ");
sb.append("sign it using privateKey.pem; embed cert.pem in the signature block; ");
sb.append("and write the data to console output:\n\n");
sb.append("\t\t-c base -l support_rim.bin -k privateKey.pem -p cert.pem -e\n\n\n");
sb.append("Create a base RIM using the values in attributes.json; " +
"sign it with the default keystore; add a RFC3852 timestamp; ");
sb.append("and write the data to base_rim.swidtag:\n\n");
sb.append("\t\t-c base -a attributes.json -d -l support_rim.bin " +
"--timestamp RFC3852 counterSignature.bin -o base_rim.swidtag\n\n\n");
sb.append("Validate a base RIM using an external support RIM to override the ");
sb.append("payload file:\n\n");
sb.append("\t\t-v base_rim.swidtag -l support_rim.bin\n\n\n");
sb.append("Validate a base RIM with its own payload file and a PEM truststore ");
sb.append("containing the signing cert:\n\n");
sb.append("\t\t-v base_rim.swidtag -t ca.crt\n\n\n");
sb.append("Create a base RIM: use the values in attributes.json; ");
sb.append("add support_rim.bin to the payload; ");
sb.append("sign it using privateKey.pem and cert.pem; embed cert.pem in the signature; ");
sb.append("add a RFC3852 timestamp; and write the data to base_rim.swidtag:\n\n");
sb.append("\t\t-c base -a attributes.json -l support_rim.bin "
+ "-k privateKey.pem -p cert.pem -e --timestamp RFC3852 counterSignature.bin "
+ "-o base_rim.swidtag\n\n\n");
sb.append("Validate base_rim.swidtag: "
+ "the payload <File> is validated with support_rim.bin; "
+ "and the signature is validated with ca.crt:\n\n");
sb.append("\t\t-v base_rim.swidtag -l support_rim.bin -t ca.crt\n\n\n");
return sb.toString();
}

View File

@ -0,0 +1,76 @@
package hirs.swid.utils;
public class CredentialArgumentValidator {
private String truststoreFile;
private String certificateFile;
private String privateKeyFile;
private String format;
private boolean isValidating;
private String errorMessage;
private static final String PEM = "PEM";
public CredentialArgumentValidator(String truststoreFile,
String certificateFile,
String privateKeyFile,
boolean isValidating) {
this.truststoreFile = truststoreFile;
this.certificateFile = certificateFile;
this.privateKeyFile = privateKeyFile;
this.isValidating = isValidating;
errorMessage = "";
}
/**
* Getter for format property
*
* @return string
*/
public String getFormat() {
return format;
}
/**
* Getter for error message
*
* @return string
*/
public String getErrorMessage() {
return errorMessage;
}
/**
* This method checks for the following valid configurations of input arguments:
* 1.
* 2. truststore only for validating (PEM format)
* 3. certificate + private key for signing (PEM format)
* 4.
*
* @return true if the above are found, false otherwise
*/
public boolean isValid() {
if (isValidating) {
if (!truststoreFile.isEmpty()) {
format = PEM;
return true;
} else {
errorMessage = "Validation requires a valid truststore file.";
return false;
}
} else {
if (!certificateFile.isEmpty() && !privateKeyFile.isEmpty()) {
format = PEM;
return true;
} else {
if (certificateFile.isEmpty()) {
errorMessage = "A public certificate must be specified by \'-p\' " +
"for signing operations.";
}
if (privateKeyFile.isEmpty()) {
errorMessage = "A private key must be specified by \'-k\' " +
"for signing operations.";
}
return false;
}
}
}
}

View File

@ -0,0 +1,33 @@
package hirs.swid.utils;
import com.beust.jcommander.IParameterValidator;
import com.beust.jcommander.ParameterException;
import java.io.File;
import lombok.extern.log4j.Log4j2;
/**
* This class validates arguments that take a String path to a file.
* The file path is checked for null, and if the file is found it is checked
* for validity, emptiness, and read permissions.
*/
@Log4j2
public class FileArgumentValidator implements IParameterValidator {
public void validate(String name, String value) throws ParameterException {
try {
File file = new File(value);
if (!file.isFile()) {
throw new ParameterException("Invalid file path: " + value +
". Please verify file path.");
}
if (file.length() == 0) {
throw new ParameterException("File " + value + " is empty.");
}
} catch (NullPointerException e) {
throw new ParameterException("File path cannot be null: " + e.getMessage());
} catch (SecurityException e) {
throw new ParameterException("Read access denied for " + value +
", please verify permissions.");
}
}
}