Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[#709] Implementing LDevID generation #814

Merged
merged 8 commits into from
Aug 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -146,4 +146,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
Loading