Skip to content

Commit

Permalink
Implementing LDevID generation (#814)
Browse files Browse the repository at this point in the history
  • Loading branch information
iadgovuser59 authored Aug 19, 2024
1 parent a62e45e commit 3de50b0
Show file tree
Hide file tree
Showing 24 changed files with 365 additions and 54 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# compiled python for systems tests
# compiled python for systems testsG
*.pyc
*.pydevproject

Expand Down Expand Up @@ -147,4 +147,5 @@ HIRS_Provisioner.NET/**/.vs
HIRS_Provisioner.NET/**/bin
HIRS_Provisioner.NET/**/generated
HIRS_Provisioner.NET/**/obj
HIRS_Provisioner.NET/**/plugins
HIRS_Provisioner.NET/**/PublishProfiles
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ public AttestationCertificateAuthority(

this.certificateRequestHandler = new CertificateRequestProcessor(supplyChainValidationService,
certificateRepository, deviceRepository,
privateKey, acaCertificate, validDays, tpm2ProvisionerStateRepository);
privateKey, acaCertificate, validDays, tpm2ProvisionerStateRepository, policyRepository);
this.identityClaimHandler = new IdentityClaimProcessor(supplyChainValidationService,
certificateRepository, componentResultRepository, componentInfoRepository,
referenceManifestRepository, referenceDigestValueRepository,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
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 org.springframework.data.domain.Sort;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
Expand Down Expand Up @@ -35,5 +36,6 @@ public interface CertificateRepository extends JpaRepository<Certificate, UUID>
Certificate findByCertificateHash(int certificateHash, String dType);
EndorsementCredential findByPublicKeyModulusHexValue(String publicKeyModulusHexValue);
IssuedAttestationCertificate findByDeviceId(UUID deviceId);
List<IssuedAttestationCertificate> findByDeviceIdAndIsLDevID(UUID deviceId, boolean isLDevID, Sort sort);
Certificate findByCertificateHash(int certificateHash);
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package hirs.attestationca.persist.entity.userdefined.certificate;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.JoinColumn;
Expand Down Expand Up @@ -35,6 +36,9 @@ public class IssuedAttestationCertificate extends DeviceAssociatedCertificate {
@JoinColumn(name = "pc_id")
private List<PlatformCredential> platformCredentials;

@Column
public boolean isLDevID;

/**
* Constructor.
* @param certificateBytes the issued certificate bytes
Expand All @@ -44,11 +48,12 @@ public class IssuedAttestationCertificate extends DeviceAssociatedCertificate {
*/
public IssuedAttestationCertificate(final byte[] certificateBytes,
final EndorsementCredential endorsementCredential,
final List<PlatformCredential> platformCredentials)
final List<PlatformCredential> platformCredentials, boolean isLDevID)
throws IOException {
super(certificateBytes);
this.endorsementCredential = endorsementCredential;
this.platformCredentials = new ArrayList<>(platformCredentials);
this.isLDevID = isLDevID;
}

/**
Expand All @@ -60,9 +65,10 @@ public IssuedAttestationCertificate(final byte[] certificateBytes,
*/
public IssuedAttestationCertificate(final Path certificatePath,
final EndorsementCredential endorsementCredential,
final List<PlatformCredential> platformCredentials)
final List<PlatformCredential> platformCredentials,
final boolean isLDevID)
throws IOException {
this(readBytes(certificatePath), endorsementCredential, platformCredentials);
this(readBytes(certificatePath), endorsementCredential, platformCredentials, isLDevID);
}

public List<PlatformCredential> getPlatformCredentials() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.springframework.data.domain.Sort;

import java.io.IOException;
import java.math.BigInteger;
Expand Down Expand Up @@ -234,15 +235,17 @@ private EndorsementCredential getEndorsementCredential(
* @param endorsementCredential the endorsement credential used to generate the AC
* @param platformCredentials the platform credentials used to generate the AC
* @param device the device to which the attestation certificate is tied
* @param isLDevID whether the certificate is a ldevid
* @return whether the certificate was saved successfully
* @throws {@link CertificateProcessingException} if error occurs in persisting the Attestation
* Certificate
*/
public void saveAttestationCertificate(final CertificateRepository certificateRepository,
public boolean saveAttestationCertificate(final CertificateRepository certificateRepository,
final byte[] derEncodedAttestationCertificate,
final EndorsementCredential endorsementCredential,
final List<PlatformCredential> platformCredentials,
final Device device) {
IssuedAttestationCertificate issuedAc;
final Device device, boolean isLDevID) {
List<IssuedAttestationCertificate> issuedAc;
boolean generateCertificate = true;
PolicyRepository scp = getPolicyRepository();
PolicySettings policySettings;
Expand All @@ -251,26 +254,33 @@ public void saveAttestationCertificate(final CertificateRepository certificateRe
try {
// save issued certificate
IssuedAttestationCertificate attCert = new IssuedAttestationCertificate(
derEncodedAttestationCertificate, endorsementCredential, platformCredentials);
derEncodedAttestationCertificate, endorsementCredential, platformCredentials, isLDevID);

if (scp != null) {
policySettings = scp.findByName("Default");
issuedAc = certificateRepository.findByDeviceId(device.getId());

generateCertificate = policySettings.isIssueAttestationCertificate();
if (issuedAc != null && policySettings.isGenerateOnExpiration()) {
if (issuedAc.getEndValidity().after(currentDate)) {
Sort sortCriteria = Sort.by(Sort.Direction.DESC, "endValidity");
issuedAc = certificateRepository.findByDeviceIdAndIsLDevID(device.getId(), isLDevID, sortCriteria);

generateCertificate = isLDevID ? policySettings.isIssueDevIdCertificate()
: policySettings.isIssueAttestationCertificate();

if (issuedAc != null && issuedAc.size() > 0 && (isLDevID ? policySettings.isDevIdExpirationFlag()
: policySettings.isGenerateOnExpiration())) {
if (issuedAc.get(0).getEndValidity().after(currentDate)) {
// so the issued AC is not expired
// however are we within the threshold
days = ProvisionUtils.daysBetween(currentDate, issuedAc.getEndValidity());
if (days < Integer.parseInt(policySettings.getReissueThreshold())) {
days = ProvisionUtils.daysBetween(currentDate, issuedAc.get(0).getEndValidity());
if (days < Integer.parseInt(isLDevID ? policySettings.getDevIdReissueThreshold()
: policySettings.getReissueThreshold())) {
generateCertificate = true;
} else {
generateCertificate = false;
}
}
}
}

if (generateCertificate) {
attCert.setDeviceId(device.getId());
attCert.setDeviceName(device.getName());
Expand All @@ -282,6 +292,8 @@ public void saveAttestationCertificate(final CertificateRepository certificateRe
"Encountered error while storing Attestation Certificate: "
+ e.getMessage(), e);
}

return generateCertificate;
}

private List<PlatformCredential> getPlatformCredentials(final CertificateRepository certificateRepository,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import hirs.attestationca.configuration.provisionerTpm2.ProvisionerTpm2;
import hirs.attestationca.persist.entity.manager.CertificateRepository;
import hirs.attestationca.persist.entity.manager.DeviceRepository;
import hirs.attestationca.persist.entity.manager.PolicyRepository;
import hirs.attestationca.persist.entity.manager.TPM2ProvisionerStateRepository;
import hirs.attestationca.persist.entity.tpm.TPM2ProvisionerState;
import hirs.attestationca.persist.entity.userdefined.Device;
Expand Down Expand Up @@ -44,20 +45,23 @@ public class CertificateRequestProcessor extends AbstractProcessor {
* @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.
* @param policyRepository db connector for policies.
*/
public CertificateRequestProcessor(final SupplyChainValidationService supplyChainValidationService,
final CertificateRepository certificateRepository,
final DeviceRepository deviceRepository,
final PrivateKey privateKey,
final X509Certificate acaCertificate,
final int validDays,
final TPM2ProvisionerStateRepository tpm2ProvisionerStateRepository) {
final TPM2ProvisionerStateRepository tpm2ProvisionerStateRepository,
final PolicyRepository policyRepository) {
super(privateKey, validDays);
this.supplyChainValidationService = supplyChainValidationService;
this.certificateRepository = certificateRepository;
this.deviceRepository = deviceRepository;
this.acaCertificate = acaCertificate;
this.tpm2ProvisionerStateRepository = tpm2ProvisionerStateRepository;
setPolicyRepository(policyRepository);
}

/**
Expand Down Expand Up @@ -107,6 +111,12 @@ public byte[] processCertificateRequest(final byte[] certificateRequest) {
List<PlatformCredential> platformCredentials = parsePcsFromIdentityClaim(claim,
endorsementCredential, certificateRepository);

// Get LDevID public key if it exists
RSAPublicKey ldevidPub = null;
if (claim.hasLdevidPublicArea()) {
ldevidPub = ProvisionUtils.parsePublicKey(claim.getLdevidPublicArea().toByteArray());
}

// Get device name and device
String deviceName = claim.getDv().getNw().getHostname();
Device device = deviceRepository.findByName(deviceName);
Expand Down Expand Up @@ -142,24 +152,63 @@ public byte[] processCertificateRequest(final byte[] certificateRequest) {
// Create signed, attestation certificate
X509Certificate attestationCertificate = generateCredential(akPub,
endorsementCredential, platformCredentials, deviceName, acaCertificate);
byte[] derEncodedAttestationCertificate = ProvisionUtils.getDerEncodedCertificate(
attestationCertificate);
if (ldevidPub != null) {
// Create signed LDevID certificate
X509Certificate ldevidCertificate = generateCredential(ldevidPub,
endorsementCredential, platformCredentials, deviceName, acaCertificate);
byte[] derEncodedAttestationCertificate = ProvisionUtils.getDerEncodedCertificate(
attestationCertificate);
byte[] derEncodedLdevidCertificate = ProvisionUtils.getDerEncodedCertificate(
ldevidCertificate);

// We validated the nonce and made use of the identity claim so state can be deleted
tpm2ProvisionerStateRepository.delete(tpm2ProvisionerState);
// We validated the nonce and made use of the identity claim so state can be deleted
tpm2ProvisionerStateRepository.delete(tpm2ProvisionerState);

// Package the signed certificate into a response
ByteString certificateBytes = ByteString
.copyFrom(derEncodedAttestationCertificate);
ProvisionerTpm2.CertificateResponse response = ProvisionerTpm2.CertificateResponse
.newBuilder().setCertificate(certificateBytes)
.setStatus(ProvisionerTpm2.ResponseStatus.PASS)
.build();
// Package the signed certificates into a response
ByteString certificateBytes = ByteString
.copyFrom(derEncodedAttestationCertificate);
ByteString ldevidCertificateBytes = ByteString
.copyFrom(derEncodedLdevidCertificate);

saveAttestationCertificate(certificateRepository, derEncodedAttestationCertificate,
endorsementCredential, platformCredentials, device);
boolean generateAtt = saveAttestationCertificate(certificateRepository, derEncodedAttestationCertificate,
endorsementCredential, platformCredentials, device, false);
boolean generateLDevID = saveAttestationCertificate(certificateRepository, derEncodedLdevidCertificate,
endorsementCredential, platformCredentials, device, true);

return response.toByteArray();
ProvisionerTpm2.CertificateResponse.Builder builder = ProvisionerTpm2.CertificateResponse.
newBuilder().setStatus(ProvisionerTpm2.ResponseStatus.PASS);
if (generateAtt) {
builder = builder.setCertificate(certificateBytes);
}
if (generateLDevID) {
builder = builder.setLdevidCertificate(ldevidCertificateBytes);
}
ProvisionerTpm2.CertificateResponse response = builder.build();

return response.toByteArray();
}
else {
byte[] derEncodedAttestationCertificate = ProvisionUtils.getDerEncodedCertificate(
attestationCertificate);

// We validated the nonce and made use of the identity claim so state can be deleted
tpm2ProvisionerStateRepository.delete(tpm2ProvisionerState);

// Package the signed certificates into a response
ByteString certificateBytes = ByteString
.copyFrom(derEncodedAttestationCertificate);
ProvisionerTpm2.CertificateResponse.Builder builder = ProvisionerTpm2.CertificateResponse.
newBuilder().setStatus(ProvisionerTpm2.ResponseStatus.PASS);

boolean generateAtt = saveAttestationCertificate(certificateRepository, derEncodedAttestationCertificate,
endorsementCredential, platformCredentials, device, false);
if (generateAtt) {
builder = builder.setCertificate(certificateBytes);
}
ProvisionerTpm2.CertificateResponse response = builder.build();

return response.toByteArray();
}
} else {
log.error("Supply chain validation did not succeed. "
+ "Firmware Quote Validation failed. Result is: "
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ public static <T extends ArchivableEntity> Certificate getTestCertificate(
return new PlatformCredential(certPath);
case "IssuedAttestationCertificate":
return new IssuedAttestationCertificate(certPath,
endorsementCredential, platformCredentials);
endorsementCredential, platformCredentials, false);
default:
throw new IllegalArgumentException(
String.format("Unknown certificate class %s", certificateClass.getName())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
<thead>
<tr>
<th rowspan="2">Hostname</th>
<th rowspan="2">Type</th>
<th rowspan="2">Issuer</th>
<th rowspan="2">Valid (begin)</th>
<th rowspan="2">Valid (end)</th>
Expand All @@ -51,6 +52,16 @@
return full.deviceName;
}
},
{
data: 'isLDevID',
searchable:false,
render: function (data, type, full, meta) {
if (data === true) {
return "LDevID";
}
return "AK";
}
},
{data: 'issuer'},
{
data: 'beginValidity',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@
<br />

<%-- Generate LDevID Certificate--%>
<div class="aca-input-box" style="display: none">
<div class="aca-input-box">
<form:form method="POST" modelAttribute="initialData" action="policy/update-issue-devid">
<li>Generate LDevID Certificate: ${initialData.issueDevIdCertificate ? 'Enabled' : 'Disabled'}
<my:editor id="issuedDevIdCertificatePolicyEditor" label="Edit Settings">
Expand Down
2 changes: 1 addition & 1 deletion HIRS_AttestationCAPortal/src/main/webapp/common/common.js
Original file line number Diff line number Diff line change
Expand Up @@ -215,4 +215,4 @@ function formatCertificateDate(dateText) {
}

return new Date(date).toUTCString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ public <T extends Certificate> Certificate getTestCertificate(
return new CertificateAuthorityCredential(fPath);
case "IssuedAttestationCertificate":
return new IssuedAttestationCertificate(fPath,
endorsementCredential, platformCredentials);
endorsementCredential, platformCredentials, false);
default:
throw new IllegalArgumentException(
String.format("Unknown certificate class %s", certificateClass.getName())
Expand Down
2 changes: 1 addition & 1 deletion HIRS_Provisioner.NET/hirs/Directory.Build.targets
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
<Target Name="SetWixPath" BeforeTargets="Msi">
<PropertyGroup>
<ProductSourceFilePath>$(MSBuildThisFileDirectory)\Resources\Product.wxs</ProductSourceFilePath>
<WixInstallPath>$(NuGetPackageRoot)wix\3.14.0\tools\</WixInstallPath>
<WixInstallPath>$(NuGetPackageRoot)wix\3.14.1\tools\</WixInstallPath>
<Heat>$(WixInstallPath)heat.exe</Heat>
<Candle>$(WixInstallPath)candle.exe</Candle>
<Light>$(WixInstallPath)light.exe</Light>
Expand Down
Loading

0 comments on commit 3de50b0

Please sign in to comment.