diff --git a/.gitignore b/.gitignore index 1b6f15f82..2c53397cb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ -# compiled python for systems tests +# compiled python for systems testsG *.pyc *.pydevproject @@ -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 diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/AttestationCertificateAuthority.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/AttestationCertificateAuthority.java index c1b898db2..6ca834f8b 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/AttestationCertificateAuthority.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/AttestationCertificateAuthority.java @@ -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, diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/manager/CertificateRepository.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/manager/CertificateRepository.java index 900a30a64..cfdfd10ac 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/manager/CertificateRepository.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/manager/CertificateRepository.java @@ -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; @@ -35,5 +36,6 @@ public interface CertificateRepository extends JpaRepository Certificate findByCertificateHash(int certificateHash, String dType); EndorsementCredential findByPublicKeyModulusHexValue(String publicKeyModulusHexValue); IssuedAttestationCertificate findByDeviceId(UUID deviceId); + List findByDeviceIdAndIsLDevID(UUID deviceId, boolean isLDevID, Sort sort); Certificate findByCertificateHash(int certificateHash); } diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/IssuedAttestationCertificate.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/IssuedAttestationCertificate.java index e37bd7c80..8c4c0e418 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/IssuedAttestationCertificate.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/userdefined/certificate/IssuedAttestationCertificate.java @@ -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; @@ -35,6 +36,9 @@ public class IssuedAttestationCertificate extends DeviceAssociatedCertificate { @JoinColumn(name = "pc_id") private List platformCredentials; + @Column + public boolean isLDevID; + /** * Constructor. * @param certificateBytes the issued certificate bytes @@ -44,11 +48,12 @@ public class IssuedAttestationCertificate extends DeviceAssociatedCertificate { */ public IssuedAttestationCertificate(final byte[] certificateBytes, final EndorsementCredential endorsementCredential, - final List platformCredentials) + final List platformCredentials, boolean isLDevID) throws IOException { super(certificateBytes); this.endorsementCredential = endorsementCredential; this.platformCredentials = new ArrayList<>(platformCredentials); + this.isLDevID = isLDevID; } /** @@ -60,9 +65,10 @@ public IssuedAttestationCertificate(final byte[] certificateBytes, */ public IssuedAttestationCertificate(final Path certificatePath, final EndorsementCredential endorsementCredential, - final List platformCredentials) + final List platformCredentials, + final boolean isLDevID) throws IOException { - this(readBytes(certificatePath), endorsementCredential, platformCredentials); + this(readBytes(certificatePath), endorsementCredential, platformCredentials, isLDevID); } public List getPlatformCredentials() { diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/AbstractProcessor.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/AbstractProcessor.java index 5a305d970..0f59637a5 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/AbstractProcessor.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/AbstractProcessor.java @@ -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; @@ -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 platformCredentials, - final Device device) { - IssuedAttestationCertificate issuedAc; + final Device device, boolean isLDevID) { + List issuedAc; boolean generateCertificate = true; PolicyRepository scp = getPolicyRepository(); PolicySettings policySettings; @@ -251,19 +254,25 @@ 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; @@ -271,6 +280,7 @@ public void saveAttestationCertificate(final CertificateRepository certificateRe } } } + if (generateCertificate) { attCert.setDeviceId(device.getId()); attCert.setDeviceName(device.getName()); @@ -282,6 +292,8 @@ public void saveAttestationCertificate(final CertificateRepository certificateRe "Encountered error while storing Attestation Certificate: " + e.getMessage(), e); } + + return generateCertificate; } private List getPlatformCredentials(final CertificateRepository certificateRepository, diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/CertificateRequestProcessor.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/CertificateRequestProcessor.java index 793187f97..571e8ae04 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/CertificateRequestProcessor.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/CertificateRequestProcessor.java @@ -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; @@ -44,6 +45,7 @@ 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, @@ -51,13 +53,15 @@ public CertificateRequestProcessor(final SupplyChainValidationService supplyChai 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); } /** @@ -107,6 +111,12 @@ public byte[] processCertificateRequest(final byte[] certificateRequest) { List 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); @@ -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: " diff --git a/HIRS_AttestationCA/src/test/java/hirs/attestationca/persist/entity/userdefined/AbstractUserdefinedEntityTest.java b/HIRS_AttestationCA/src/test/java/hirs/attestationca/persist/entity/userdefined/AbstractUserdefinedEntityTest.java index 01f1c7fb3..56ff99c82 100644 --- a/HIRS_AttestationCA/src/test/java/hirs/attestationca/persist/entity/userdefined/AbstractUserdefinedEntityTest.java +++ b/HIRS_AttestationCA/src/test/java/hirs/attestationca/persist/entity/userdefined/AbstractUserdefinedEntityTest.java @@ -160,7 +160,7 @@ public static 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()) diff --git a/HIRS_AttestationCAPortal/src/main/webapp/WEB-INF/jsp/issued-certificates.jsp b/HIRS_AttestationCAPortal/src/main/webapp/WEB-INF/jsp/issued-certificates.jsp index 0c37bc8c4..c82a00ca3 100644 --- a/HIRS_AttestationCAPortal/src/main/webapp/WEB-INF/jsp/issued-certificates.jsp +++ b/HIRS_AttestationCAPortal/src/main/webapp/WEB-INF/jsp/issued-certificates.jsp @@ -26,6 +26,7 @@ Hostname + Type Issuer Valid (begin) Valid (end) @@ -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', diff --git a/HIRS_AttestationCAPortal/src/main/webapp/WEB-INF/jsp/policy.jsp b/HIRS_AttestationCAPortal/src/main/webapp/WEB-INF/jsp/policy.jsp index 077638da5..64ed88e90 100644 --- a/HIRS_AttestationCAPortal/src/main/webapp/WEB-INF/jsp/policy.jsp +++ b/HIRS_AttestationCAPortal/src/main/webapp/WEB-INF/jsp/policy.jsp @@ -189,7 +189,7 @@
<%-- Generate LDevID Certificate--%> -