From 8964f31aea5ba3a1922b6f215953632c05c8ec79 Mon Sep 17 00:00:00 2001 From: ThatSilentCoder <184309164+ThatSilentCoder@users.noreply.github.com> Date: Thu, 19 Feb 2026 17:51:18 -0500 Subject: [PATCH 01/25] v3.1_issue_1090: First cut of the fixing the ecc provisioning issue. --- .../AttestationCertificateAuthority.java | 7 +- ...estfulAttestationCertificateAuthority.java | 7 +- .../persist/RestfulInterface.java | 4 +- .../persist/enums/PublicKeyAlgorithm.java | 34 ++++ .../CertificateRequestProcessor.java | 30 ++-- .../provision/IdentityClaimProcessor.java | 4 +- .../provision/helper/ProvisionUtils.java | 156 ++++++++++-------- .../AttestationCertificateAuthorityTest.java | 86 +++++----- .../converters/SimpleStructBuilder.java | 2 +- 9 files changed, 195 insertions(+), 135 deletions(-) create mode 100644 HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/enums/PublicKeyAlgorithm.java 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 f45c7269e..c34dbeb36 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/AttestationCertificateAuthority.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/AttestationCertificateAuthority.java @@ -15,6 +15,7 @@ import hirs.structs.converters.StructConverter; import lombok.extern.log4j.Log4j2; +import java.security.GeneralSecurityException; import java.security.PrivateKey; import java.security.cert.X509Certificate; @@ -23,7 +24,7 @@ * Identity Request. */ @Log4j2 -public abstract class AttestationCertificateAuthority { +public abstract class AttestationCertificateAuthority implements RestfulInterface { /** * Container wired ACA private key. @@ -125,7 +126,7 @@ public AttestationCertificateAuthority( * @param identityClaim a byte array representation of the identity claim * @return processed identity claim response */ - byte[] processIdentityClaimTpm2(final byte[] identityClaim) { + public byte[] processIdentityClaimTpm2(final byte[] identityClaim) { return this.identityClaimHandler.processIdentityClaimTpm2(identityClaim); } @@ -135,7 +136,7 @@ byte[] processIdentityClaimTpm2(final byte[] identityClaim) { * @param certificateRequest a byte array representation of the certificate request * @return processed certificate request response */ - byte[] processCertificateRequest(final byte[] certificateRequest) { + public byte[] processCertificateRequest(final byte[] certificateRequest) throws GeneralSecurityException { return this.certificateRequestHandler.processCertificateRequest(certificateRequest); } diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/RestfulAttestationCertificateAuthority.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/RestfulAttestationCertificateAuthority.java index 58a3b42f1..5bafc5b92 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/RestfulAttestationCertificateAuthority.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/RestfulAttestationCertificateAuthority.java @@ -25,6 +25,7 @@ import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; +import java.security.GeneralSecurityException; import java.security.PrivateKey; import java.security.cert.X509Certificate; @@ -42,9 +43,7 @@ @Log4j2 @RestController @RequestMapping("/HIRS_AttestationCA") -public class RestfulAttestationCertificateAuthority extends AttestationCertificateAuthority - implements RestfulInterface { - +public class RestfulAttestationCertificateAuthority extends AttestationCertificateAuthority { /** * Constructor. * @@ -118,7 +117,7 @@ public byte[] processIdentityClaimTpm2(@RequestBody final byte[] identityClaim) @ResponseBody @PostMapping(value = "/request-certificate-tpm2", consumes = MediaType.APPLICATION_OCTET_STREAM_VALUE) - public byte[] processCertificateRequest(@RequestBody final byte[] certificateRequest) { + public byte[] processCertificateRequest(@RequestBody final byte[] certificateRequest) throws GeneralSecurityException { return super.processCertificateRequest(certificateRequest); } diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/RestfulInterface.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/RestfulInterface.java index a4bb8a00a..0c8bf7766 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/RestfulInterface.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/RestfulInterface.java @@ -1,5 +1,7 @@ package hirs.attestationca.persist; +import java.security.GeneralSecurityException; + /** * Defines the responsibilities of the Attestation Certificate Authority. */ @@ -19,6 +21,6 @@ public interface RestfulInterface { * @param certificateRequest a byte array representation of the certificate request * @return a byte array representation of the certificate request response */ - byte[] processCertificateRequest(byte[] certificateRequest); + byte[] processCertificateRequest(byte[] certificateRequest) throws GeneralSecurityException; } diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/enums/PublicKeyAlgorithm.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/enums/PublicKeyAlgorithm.java new file mode 100644 index 000000000..1842b61b7 --- /dev/null +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/enums/PublicKeyAlgorithm.java @@ -0,0 +1,34 @@ +package hirs.attestationca.persist.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.ToString; + +import java.security.PublicKey; +import java.security.interfaces.ECPublicKey; +import java.security.interfaces.RSAPublicKey; + +/** + * Enum representing public key algorithms for asymmetric cryptography. + *

+ * This enum currently includes: + *

+ *

+ * Each enum constant also holds its corresponding JCA (Java Cryptography Architecture) + * standard name, which can be used when creating KeyFactory or Cipher instances. + */ +@Getter +@AllArgsConstructor +@ToString +public enum PublicKeyAlgorithm { + RSA("RSA", RSAPublicKey.class), + EC("EC", ECPublicKey.class), + UNKNOWN("", null); + + private final String algorithmName; + private final Class keyClass; +} + 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 cc9e0b197..f51c53ae5 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 @@ -23,9 +23,10 @@ import org.apache.commons.lang3.ArrayUtils; import java.nio.charset.StandardCharsets; +import java.security.GeneralSecurityException; import java.security.PrivateKey; +import java.security.PublicKey; import java.security.cert.X509Certificate; -import java.security.interfaces.RSAPublicKey; import java.util.List; @Log4j2 @@ -75,12 +76,13 @@ public CertificateRequestProcessor(final SupplyChainValidationService supplyChai * claim handshake * @return a certificateResponse containing the signed certificate */ - public byte[] processCertificateRequest(final byte[] certificateRequest) { + public byte[] processCertificateRequest(final byte[] certificateRequest) throws GeneralSecurityException { log.info("Certificate Request has been received and is ready to be processed"); if (ArrayUtils.isEmpty(certificateRequest)) { - throw new IllegalArgumentException("The CertificateRequest sent by the client" - + " cannot be null or empty."); + final String errorMsg = "The CertificateRequest sent by the client cannot be null or empty."; + log.error(errorMsg); + throw new IllegalArgumentException(errorMsg); } final PolicyRepository policyRepository = this.getPolicyRepository(); @@ -91,15 +93,16 @@ public byte[] processCertificateRequest(final byte[] certificateRequest) { try { request = ProvisionerTpm2.CertificateRequest.parseFrom(certificateRequest); } catch (InvalidProtocolBufferException ipbe) { - throw new CertificateProcessingException( - "Could not deserialize Protobuf Certificate Request object.", ipbe); + final String errorMsg = "Could not deserialize Protobuf Certificate Request object."; + log.error(errorMsg); + throw new CertificateProcessingException(errorMsg, ipbe); } String certificateRequestJsonString = ""; try { certificateRequestJsonString = JsonFormat.printer().print(request); } catch (InvalidProtocolBufferException exception) { - log.error("Certificate request could not be parsed properly into a json string"); + log.error("Certificate request could not be parsed properly into a JSON string"); } // attempt to retrieve provisioner state based on nonce in request @@ -111,23 +114,22 @@ public byte[] processCertificateRequest(final byte[] certificateRequest) { ProvisionerTpm2.IdentityClaim claim = ProvisionUtils.parseIdentityClaim(identityClaim); // Get endorsement public key - RSAPublicKey ekPub = ProvisionUtils.parsePublicKey(claim.getEkPublicArea().toByteArray()); + PublicKey ekPub = ProvisionUtils.parsePublicKeyFromPublicDataSegment(claim.getEkPublicArea().toByteArray()); // Get attestation public key - RSAPublicKey akPub = ProvisionUtils.parsePublicKey(claim.getAkPublicArea().toByteArray()); + PublicKey akPub = ProvisionUtils.parsePublicKeyFromPublicDataSegment(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 platformCredentials = parsePcsFromIdentityClaim(claim, endorsementCredential, certificateRepository); // Get LDevID public key if it exists - RSAPublicKey ldevidPub = null; + PublicKey ldevidPub = null; if (claim.hasLdevidPublicArea()) { - ldevidPub = ProvisionUtils.parsePublicKey(claim.getLdevidPublicArea().toByteArray()); + ldevidPub = ProvisionUtils.parsePublicKeyFromPublicDataSegment(claim.getLdevidPublicArea().toByteArray()); } // Get device name and device @@ -136,7 +138,7 @@ public byte[] processCertificateRequest(final byte[] certificateRequest) { // Parse through the Provisioner supplied TPM Quote and pcr values // these fields are optional - if (request.getQuote() != null && !request.getQuote().isEmpty()) { + if (!request.getQuote().isEmpty()) { TPMInfo savedInfo = device.getDeviceInfo().getTpmInfo(); TPMInfo tpmInfo = new TPMInfo(savedInfo.getTpmMake(), savedInfo.getTpmVersionMajor(), diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/IdentityClaimProcessor.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/IdentityClaimProcessor.java index 4866ff654..8252ce2d7 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/IdentityClaimProcessor.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/IdentityClaimProcessor.java @@ -156,7 +156,7 @@ public byte[] processIdentityClaimTpm2(final byte[] identityClaim) { // parse the EK Public key from the IdentityClaim once for use in supply chain validation // and later tpm20MakeCredential function - RSAPublicKey ekPub = ProvisionUtils.parsePublicKey(claim.getEkPublicArea().toByteArray()); + RSAPublicKey ekPub = ProvisionUtils.parseRSAKeyFromPublicDataSegment(claim.getEkPublicArea().toByteArray()); AppraisalStatus.Status validationResult = AppraisalStatus.Status.FAIL; try { @@ -168,7 +168,7 @@ public byte[] processIdentityClaimTpm2(final byte[] identityClaim) { ByteString blobStr = ByteString.copyFrom(new byte[]{}); if (validationResult == AppraisalStatus.Status.PASS) { - RSAPublicKey akPub = ProvisionUtils.parsePublicKey(claim.getAkPublicArea().toByteArray()); + RSAPublicKey akPub = ProvisionUtils.parseRSAKeyFromPublicDataSegment(claim.getAkPublicArea().toByteArray()); byte[] nonce = ProvisionUtils.generateRandomBytes(NONCE_LENGTH); blobStr = ProvisionUtils.tpm20MakeCredential(ekPub, akPub, nonce); diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/helper/ProvisionUtils.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/helper/ProvisionUtils.java index 88c747fe7..4ff13866b 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/helper/ProvisionUtils.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/helper/ProvisionUtils.java @@ -3,13 +3,13 @@ import com.google.protobuf.ByteString; import com.google.protobuf.InvalidProtocolBufferException; import hirs.attestationca.configuration.provisionerTpm2.ProvisionerTpm2; +import hirs.attestationca.persist.enums.PublicKeyAlgorithm; import hirs.attestationca.persist.exceptions.CertificateProcessingException; import hirs.attestationca.persist.exceptions.IdentityProcessingException; import hirs.attestationca.persist.exceptions.UnexpectedServerException; import hirs.structs.converters.SimpleStructBuilder; import hirs.structs.elements.aca.SymmetricAttestation; import hirs.structs.elements.tpm.EncryptionScheme; -import hirs.structs.elements.tpm.IdentityRequest; import hirs.structs.elements.tpm.SymmetricKey; import hirs.structs.elements.tpm.SymmetricKeyParams; import hirs.utils.HexUtils; @@ -32,6 +32,7 @@ import java.math.BigInteger; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; +import java.security.GeneralSecurityException; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.KeyFactory; @@ -42,10 +43,12 @@ import java.security.SecureRandom; import java.security.cert.CertificateEncodingException; import java.security.cert.X509Certificate; +import java.security.interfaces.ECPublicKey; import java.security.interfaces.RSAPublicKey; import java.security.spec.InvalidKeySpecException; import java.security.spec.MGF1ParameterSpec; import java.security.spec.RSAPublicKeySpec; +import java.security.spec.X509EncodedKeySpec; import java.util.Date; @Log4j2 @@ -112,7 +115,6 @@ private ProvisionUtils() { * @param identityClaim byte array that should be converted to a Protobuf IdentityClaim * object * @return the Protobuf generated Identity Claim object - * @throws {@link InvalidProtocolBufferException} if byte array could not be parsed */ public static ProvisionerTpm2.IdentityClaim parseIdentityClaim(final byte[] identityClaim) { try { @@ -128,7 +130,6 @@ public static ProvisionerTpm2.IdentityClaim parseIdentityClaim(final byte[] iden * * @param certificate the X509 certificate to be converted to DER encoding * @return the byte array representing the DER encoded certificate - * @throws {@link UnexpectedServerException} if error occurs during encoding retrieval */ public static byte[] getDerEncodedCertificate(final X509Certificate certificate) { try { @@ -145,7 +146,6 @@ public static byte[] getDerEncodedCertificate(final X509Certificate certificate) * Helper method to extract a PEM encoded certificate from an X509 certificate. * * @param certificate the X509 certificate to be converted to PEM encoding - * @throws {@link UnexpectedServerException} if error occurs during encoding retrieval * @return the string representing the PEM encoded certificate */ public static String getPemEncodedCertificate(final X509Certificate certificate) { @@ -165,13 +165,31 @@ public static String getPemEncodedCertificate(final X509Certificate certificate) } /** - * Parse public key from public data segment generated by TPM 2.0. + * Parses a public key from a byte array and returns it as a generic PublicKey. + * Supports RSA and EC (Elliptic Curve) keys. + * + * @param keyBytes the DER-encoded public key bytes + * @return the parsed PublicKey instance + * @throws GeneralSecurityException if the key cannot be parsed + */ + public static PublicKey parsePublicKeyFromPublicDataSegment(byte[] keyBytes) throws GeneralSecurityException { + PublicKeyAlgorithm publicKeyAlgorithm = determineKeyType(keyBytes); + + return switch (publicKeyAlgorithm) { + case RSA -> parseRSAKeyFromPublicDataSegment(keyBytes); + case EC -> parseECKeyFromPublicDataSegment(keyBytes); + default -> throw new GeneralSecurityException("Unsupported or invalid public key"); + }; + } + + /** + * Parses the RSA public key from public data segment generated by TPM 2.0. * * @param publicArea the public area segment to parse * @return the RSA public key of the supplied public data */ - public static RSAPublicKey parsePublicKey(final byte[] publicArea) { - int pubLen = publicArea.length; + public static RSAPublicKey parseRSAKeyFromPublicDataSegment(final byte[] publicArea) { + final int pubLen = publicArea.length; if (pubLen < RSA_MODULUS_LENGTH) { throw new IllegalArgumentException( "EK or AK public data segment is not long enough"); @@ -180,36 +198,36 @@ public static RSAPublicKey parsePublicKey(final byte[] publicArea) { byte[] modulus = HexUtils.subarray(publicArea, pubLen - RSA_MODULUS_LENGTH, pubLen - 1); - return (RSAPublicKey) assemblePublicKey(modulus); + return (RSAPublicKey) assembleRSAPublicKey(modulus); } /** - * Constructs a public key where the modulus is in raw form. + * Constructs an RSA public key where the modulus is in raw form. * * @param modulus in byte array form - * @return public key using specific modulus and the well known exponent + * @return RSA public key using specific modulus and the well known exponent */ - public static PublicKey assemblePublicKey(final byte[] modulus) { - return assemblePublicKey(Hex.encodeHexString(modulus)); + public static PublicKey assembleRSAPublicKey(final byte[] modulus) { + return assembleRSAPublicKey(Hex.encodeHexString(modulus)); } /** - * Constructs a public key where the modulus is Hex encoded. + * Constructs an RSA public key where the modulus is Hex encoded. * * @param modulus hex encoded modulus - * @return public key using specific modulus and the well known exponent + * @return RSA public key using specific modulus and the well known exponent */ - public static PublicKey assemblePublicKey(final String modulus) { - return assemblePublicKey(new BigInteger(modulus, DEFAULT_IV_SIZE)); + public static PublicKey assembleRSAPublicKey(final String modulus) { + return assembleRSAPublicKey(new BigInteger(modulus, DEFAULT_IV_SIZE)); } /** - * Assembles a public key using a defined big int modulus and the well known exponent. + * Assembles an RSA public key using a defined big int modulus and the well known exponent. * * @param modulus modulus - * @return public key using the provided integer modulus + * @return RSA public key using the provided integer modulus */ - public static PublicKey assemblePublicKey(final BigInteger modulus) { + public static PublicKey assembleRSAPublicKey(final BigInteger modulus) { // generate a key spec using mod and exp RSAPublicKeySpec keySpec = new RSAPublicKeySpec(modulus, EXPONENT); @@ -223,6 +241,16 @@ public static PublicKey assemblePublicKey(final BigInteger modulus) { } } + /** + * Parses the ECC public key from public data segment generated by TPM 2.0. + * + * @param publicArea the public area segment to parse + * @return the ECC public key of the supplied public data + */ + public static ECPublicKey parseECKeyFromPublicDataSegment(final byte[] publicArea) { + return null; + } + /** * Will attempt to decrypt the asymmetric blob that originated from an * {@link hirs.structs.elements.tpm.IdentityRequest} using the cipher transformation. @@ -290,8 +318,7 @@ public static byte[] decryptSymmetricBlob(final byte[] symmetricBlob, final byte } catch (IllegalBlockSizeException | InvalidKeyException | NoSuchAlgorithmException | BadPaddingException | NoSuchPaddingException | InvalidAlgorithmParameterException exception) { - log.error("Encountered error while decrypting symmetric blob of an identity request: " - + exception.getMessage(), exception); + log.error("Encountered error while decrypting symmetric blob of an identity request: {}", exception.getMessage(), exception); } return new byte[0]; @@ -308,12 +335,10 @@ public static SymmetricKey generateSymmetricKey() { generateRandomBytes(DEFAULT_IV_SIZE); // create a symmetric key struct for the CA contents - SymmetricKey sessionKey = - new SimpleStructBuilder<>(SymmetricKey.class) - .set("algorithmId", SymmetricKey.ALGORITHM_AES) - .set("encryptionScheme", SymmetricKey.SCHEME_CBC) - .set("key", responseSymmetricKey).build(); - return sessionKey; + return new SimpleStructBuilder<>(SymmetricKey.class) + .set("algorithmId", SymmetricKey.ALGORITHM_AES) + .set("encryptionScheme", SymmetricKey.SCHEME_CBC) + .set("key", responseSymmetricKey).build(); } /** @@ -452,35 +477,6 @@ public static byte[] generateAsymmetricContents(final byte[] identityKey, } } - /** - * Extracts the IV from the identity request. That is, take the first block of data from the - * symmetric blob and treat that as the IV. This modifies the original symmetric block. - * - * @param identityRequest to extract the IV from - * @return the IV from the identity request - */ - public static byte[] extractInitialValue(final IdentityRequest identityRequest) { - // make a reference to the symmetric blob - byte[] symmetricBlob = identityRequest.getSymmetricBlob(); - - // create the IV - byte[] iv = new byte[DEFAULT_IV_SIZE]; - - // initialize a new symmetric blob with the length of the original minus the IV - byte[] updatedBlob = new byte[symmetricBlob.length - iv.length]; - - // copy the IV out of the original symmetric blob - System.arraycopy(symmetricBlob, 0, iv, 0, iv.length); - - // copy everything but the IV out of the original blob into the new blob - System.arraycopy(symmetricBlob, iv.length, updatedBlob, 0, updatedBlob.length); - - // reassign the symmetric blob to the request. - identityRequest.setSymmetricBlob(updatedBlob); - - return iv; - } - /** * Generate the Identity Response using the identity credential and the session key. * @@ -513,17 +509,15 @@ public static SymmetricAttestation generateAttestation(final X509Certificate cre byte[] credentialBytes = ArrayUtils.addAll(credentialIV, encryptedCredential); // create attestation for identity response that contains the credential - SymmetricAttestation attestation = - new SimpleStructBuilder<>(SymmetricAttestation.class) - .set("credential", credentialBytes) - .set("algorithm", - new SimpleStructBuilder<>(SymmetricKeyParams.class) - .set("algorithmId", SymmetricKeyParams.ALGORITHM_AES) - .set("encryptionScheme", - SymmetricKeyParams.SCHEME_CBC_PKCS5PADDING) - .set("signatureScheme", 0).build()).build(); - - return attestation; + + return new SimpleStructBuilder<>(SymmetricAttestation.class) + .set("credential", credentialBytes) + .set("algorithm", + new SimpleStructBuilder<>(SymmetricKeyParams.class) + .set("algorithmId", SymmetricKeyParams.ALGORITHM_AES) + .set("encryptionScheme", + SymmetricKeyParams.SCHEME_CBC_PKCS5PADDING) + .set("signatureScheme", 0).build()).build(); } catch (BadPaddingException | IllegalBlockSizeException | NoSuchAlgorithmException | InvalidKeyException | InvalidAlgorithmParameterException | NoSuchPaddingException @@ -746,4 +740,32 @@ public static int daysBetween(final Date date1, final Date date2) { return (int) ((date2.getTime() - date1.getTime()) / (millisecondsInASecond * secondsInAnHour * hoursInADay)); } + + /** + * Helper method that determines the public key algorithm associated with the supplied byte array. + * + * @param keyBytes byte array representation of the key + * @return {@link PublicKeyAlgorithm} + */ + private static PublicKeyAlgorithm determineKeyType(byte[] keyBytes) { + // Loop over supported algorithms + for (PublicKeyAlgorithm publicKeyAlgorithm : PublicKeyAlgorithm.values()) { + if (publicKeyAlgorithm == PublicKeyAlgorithm.UNKNOWN) continue; // Skip UNKNOWN + + try { + KeyFactory keyFactory = KeyFactory.getInstance(publicKeyAlgorithm.getAlgorithmName()); + X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes); + PublicKey publicKey = keyFactory.generatePublic(keySpec); + + // Check if the parsed key matches the expected type + if (publicKeyAlgorithm.getKeyClass().isInstance(publicKey)) { + return publicKeyAlgorithm; + } + } catch (Exception ignored) { + // Ignore and try next algorithm + } + } + + return PublicKeyAlgorithm.UNKNOWN; + } } diff --git a/HIRS_AttestationCA/src/test/java/hirs/attestationca/persist/AttestationCertificateAuthorityTest.java b/HIRS_AttestationCA/src/test/java/hirs/attestationca/persist/AttestationCertificateAuthorityTest.java index 8dcca210d..2ff41e57f 100644 --- a/HIRS_AttestationCA/src/test/java/hirs/attestationca/persist/AttestationCertificateAuthorityTest.java +++ b/HIRS_AttestationCA/src/test/java/hirs/attestationca/persist/AttestationCertificateAuthorityTest.java @@ -51,7 +51,6 @@ import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.PublicKey; -import java.util.Random; import java.security.Security; import java.security.cert.X509Certificate; import java.security.interfaces.RSAPublicKey; @@ -61,6 +60,7 @@ import java.util.LinkedList; import java.util.List; import java.util.Objects; +import java.util.Random; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -121,12 +121,43 @@ public class AttestationCertificateAuthorityTest { 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"; + private final Random random = new Random(); // object in test private AttestationCertificateAuthority aca; private AccessAbstractProcessor abstractProcessor; // test key pair private KeyPair keyPair; - private Random random = new Random(); + + /** + * Creates a self-signed X.509 public-key certificate. + * + * @param pair KeyPair to create the cert for + * @return self-signed X509Certificate + */ + private static X509Certificate createSelfSignedCertificate(final KeyPair pair) { + Security.addProvider(new BouncyCastleProvider()); + final int timeRange = 10000; + X509Certificate cert = null; + try { + + X500Name issuerName = new X500Name("CN=TEST2, OU=TEST2, O=TEST2, C=TEST2"); + X500Name subjectName = new X500Name("CN=TEST, OU=TEST, O=TEST, C=TEST"); + BigInteger serialNumber = BigInteger.ONE; + Date notBefore = new Date(System.currentTimeMillis() - timeRange); + Date notAfter = new Date(System.currentTimeMillis() + timeRange); + X509v3CertificateBuilder builder = + new JcaX509v3CertificateBuilder(issuerName, serialNumber, notBefore, notAfter, + subjectName, pair.getPublic()); + ContentSigner signer = + new JcaContentSignerBuilder("SHA256WithRSA").setProvider("BC").build( + pair.getPrivate()); + return new JcaX509CertificateConverter().setProvider("BC").getCertificate( + builder.build(signer)); + } catch (Exception e) { + fail("Exception occurred while creating a cert", e); + } + return cert; + } /** * Registers bouncy castle as a security provider. Normally the JEE container will handle this, @@ -169,7 +200,7 @@ public void testProcessIdentityClaimTpm2NullRequest() { public void testGetPublicKey() { // encoded byte array to be returned by public key - byte[] encoded = new byte[] {0, 1, 0, 1, 0}; + byte[] encoded = new byte[]{0, 1, 0, 1, 0}; // create mocks for testing X509Certificate acaCertificate = mock(X509Certificate.class); @@ -285,7 +316,7 @@ private void assertTrue(final boolean b) { public void testGenerateAsymmetricContents() throws Exception { // "encoded" identity proof (returned by struct converter) - byte[] identityProofEncoded = new byte[] {0, 0, 1, 1}; + 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]; @@ -381,37 +412,6 @@ public void testGenerateAttestation() throws Exception { verifyNoMoreInteractions(certificate, symmetricKey); } - /** - * Creates a self-signed X.509 public-key certificate. - * - * @param pair KeyPair to create the cert for - * @return self-signed X509Certificate - */ - private static X509Certificate createSelfSignedCertificate(final KeyPair pair) { - Security.addProvider(new BouncyCastleProvider()); - final int timeRange = 10000; - X509Certificate cert = null; - try { - - X500Name issuerName = new X500Name("CN=TEST2, OU=TEST2, O=TEST2, C=TEST2"); - X500Name subjectName = new X500Name("CN=TEST, OU=TEST, O=TEST, C=TEST"); - BigInteger serialNumber = BigInteger.ONE; - Date notBefore = new Date(System.currentTimeMillis() - timeRange); - Date notAfter = new Date(System.currentTimeMillis() + timeRange); - X509v3CertificateBuilder builder = - new JcaX509v3CertificateBuilder(issuerName, serialNumber, notBefore, notAfter, - subjectName, pair.getPublic()); - ContentSigner signer = - new JcaContentSignerBuilder("SHA256WithRSA").setProvider("BC").build( - pair.getPrivate()); - return new JcaX509CertificateConverter().setProvider("BC").getCertificate( - builder.build(signer)); - } catch (Exception e) { - fail("Exception occurred while creating a cert", e); - } - return cert; - } - /** * Tests {@link AttestationCertificateAuthority# * AttestationCertificateAuthority(SupplyChainValidationService, PrivateKey, @@ -488,7 +488,7 @@ public void testGenerateCredential() throws Exception { } /** - * Tests {@link ProvisionUtils#assemblePublicKey(byte[])}. + * Tests {@link ProvisionUtils#assembleRSAPublicKey(byte[])}. */ @Test public void testAssemblePublicKeyUsingByteArray() { @@ -496,7 +496,7 @@ public void testAssemblePublicKeyUsingByteArray() { final BigInteger modulus = ((RSAPublicKey) keyPair.getPublic()).getModulus(); // perform test - RSAPublicKey publicKey = (RSAPublicKey) ProvisionUtils.assemblePublicKey(modulus.toByteArray()); + RSAPublicKey publicKey = (RSAPublicKey) ProvisionUtils.assembleRSAPublicKey(modulus.toByteArray()); // assert that the exponent and the modulus are the same. the exponents should be the well // known prime, 101 @@ -506,7 +506,7 @@ public void testAssemblePublicKeyUsingByteArray() { } /** - * Tests {@link ProvisionUtils#assemblePublicKey(String)}. + * Tests {@link ProvisionUtils#assembleRSAPublicKey(String)}. */ @Test public void testAssemblePublicKeyUsingHexEncodedString() { @@ -518,7 +518,7 @@ public void testAssemblePublicKeyUsingHexEncodedString() { ((RSAPublicKey) keyPair.getPublic()).getModulus().toByteArray()); // perform test - RSAPublicKey publicKey = (RSAPublicKey) ProvisionUtils.assemblePublicKey(modulusString); + RSAPublicKey publicKey = (RSAPublicKey) ProvisionUtils.assembleRSAPublicKey(modulusString); // assert that the exponent and the modulus are the same. the exponents should be the well // known prime, 101. @@ -540,7 +540,7 @@ public void testParseEk() throws URISyntaxException, IOException { byte[] ekFile = Files.readAllBytes(ekPath); - RSAPublicKey ek = ProvisionUtils.parsePublicKey(ekFile); + RSAPublicKey ek = ProvisionUtils.parseRSAKeyFromPublicDataSegment(ekFile); final int radix = 16; assertTrue(ek.getPublicExponent().equals(new BigInteger("010001", radix))); @@ -569,7 +569,7 @@ public void testParseAk() throws URISyntaxException, IOException { byte[] akFile = Files.readAllBytes(akPath); - RSAPublicKey ak = ProvisionUtils.parsePublicKey(akFile); + RSAPublicKey ak = ProvisionUtils.parseRSAKeyFromPublicDataSegment(akFile); final int radix = 16; assertTrue(ak.getPublicExponent().equals(new BigInteger("010001", radix))); @@ -633,8 +633,8 @@ public void testMakeCredential() throws URISyntaxException, IOException { byte[] ekPubFile = Files.readAllBytes(ekPubPath); byte[] akPubFile = Files.readAllBytes(akPubPath); - RSAPublicKey ekPub = ProvisionUtils.parsePublicKey(ekPubFile); - RSAPublicKey akPub = ProvisionUtils.parsePublicKey(akPubFile); + RSAPublicKey ekPub = ProvisionUtils.parseRSAKeyFromPublicDataSegment(ekPubFile); + RSAPublicKey akPub = ProvisionUtils.parseRSAKeyFromPublicDataSegment(akPubFile); // prepare the nonce and wrap it with keys final byte[] nonce = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, diff --git a/HIRS_Structs/src/main/java/hirs/structs/converters/SimpleStructBuilder.java b/HIRS_Structs/src/main/java/hirs/structs/converters/SimpleStructBuilder.java index 45a8d3175..b910dfa0f 100644 --- a/HIRS_Structs/src/main/java/hirs/structs/converters/SimpleStructBuilder.java +++ b/HIRS_Structs/src/main/java/hirs/structs/converters/SimpleStructBuilder.java @@ -13,7 +13,7 @@ * * @param the type of Struct to build */ -public class SimpleStructBuilder implements StructBuilder { +public class SimpleStructBuilder implements StructBuilder { private final Class clazz; private T struct; From 331328bd76a2c15548e5f875df7a7575511bb527 Mon Sep 17 00:00:00 2001 From: ThatSilentCoder <184309164+ThatSilentCoder@users.noreply.github.com> Date: Fri, 20 Feb 2026 13:35:38 -0500 Subject: [PATCH 02/25] v3.1_issue_1090: Added a new property that keeps track of the algorithm used throughout the application (prop has been added to the app.properties for both win and linux machines), deleted a repo that wasnt being used anywhere, changed the name of the rest controller and service class, re-wrote the workflow from rest controller to backend since it was setup incorrectly. Provisioning is sort-of crypto-agile. It can parse the rsa key without an issue, now need to work on doing the same for the ecc key. --- .../AttestationCertificateAuthority.java | 151 ------------------ ...ionCertificateAuthorityRestController.java | 72 +++++++++ ...ttestationCertificateAuthorityService.java | 60 +++++++ ...CertificateAuthorityServiceInterface.java} | 4 +- ...estfulAttestationCertificateAuthority.java | 137 ---------------- .../SupplyChainValidationRepository.java | 28 ---- ...upplyChainValidationSummaryRepository.java | 3 +- .../persist/enums/PublicKeyAlgorithm.java | 19 ++- .../CertificateProcessingException.java | 4 +- .../IdentityProcessingException.java | 4 +- .../exceptions/UnexpectedServerException.java | 4 +- .../CertificateRequestProcessor.java | 32 +++- .../provision/IdentityClaimProcessor.java | 4 + .../provision/helper/ProvisionUtils.java | 44 +---- .../service/SupplyChainValidationService.java | 7 +- .../AttestationCertificateAuthorityTest.java | 17 +- .../src/main/resources/application.properties | 1 + .../main/resources/application.win.properties | 1 + package/linux/aca/aca_setup.sh | 3 + 19 files changed, 214 insertions(+), 381 deletions(-) delete mode 100644 HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/AttestationCertificateAuthority.java create mode 100644 HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/AttestationCertificateAuthorityRestController.java create mode 100644 HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/AttestationCertificateAuthorityService.java rename HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/{RestfulInterface.java => AttestationCertificateAuthorityServiceInterface.java} (81%) delete mode 100644 HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/RestfulAttestationCertificateAuthority.java delete mode 100644 HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/manager/SupplyChainValidationRepository.java diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/AttestationCertificateAuthority.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/AttestationCertificateAuthority.java deleted file mode 100644 index c34dbeb36..000000000 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/AttestationCertificateAuthority.java +++ /dev/null @@ -1,151 +0,0 @@ -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; -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.provision.CertificateRequestProcessor; -import hirs.attestationca.persist.provision.IdentityClaimProcessor; -import hirs.attestationca.persist.service.SupplyChainValidationService; -import hirs.structs.converters.StructConverter; -import lombok.extern.log4j.Log4j2; - -import java.security.GeneralSecurityException; -import java.security.PrivateKey; -import java.security.cert.X509Certificate; - -/** - * Provides base implementation of common tasks of an ACA that are required for attestation of an - * Identity Request. - */ -@Log4j2 -public abstract class AttestationCertificateAuthority implements RestfulInterface { - - /** - * Container wired ACA private key. - */ - private final PrivateKey privateKey; - - /** - * Container wired ACA certificate. - */ - private final X509Certificate acaCertificate; - - /** - * Container wired {@link StructConverter} to be used in - * serialization / deserialization of TPM data structures. - */ - private final StructConverter structConverter; - - /** - * A handle to the service used to validate the supply chain. - */ - private final SupplyChainValidationService supplyChainValidationService; - private final ComponentResultRepository componentResultRepository; - private final CertificateRepository certificateRepository; - private final IssuedCertificateRepository issuedCertificateRepository; - private final ReferenceManifestRepository referenceManifestRepository; - private final DeviceRepository deviceRepository; - // private final DBManager tpm2ProvisionerStateDBManager; - private final ReferenceDigestValueRepository referenceDigestValueRepository; - private final PolicyRepository policyRepository; - private final TPM2ProvisionerStateRepository tpm2ProvisionerStateRepository; - private final ComponentInfoRepository componentInfoRepository; - private final CertificateRequestProcessor certificateRequestHandler; - private final IdentityClaimProcessor identityClaimHandler; - /** - * Container wired application configuration property identifying the number of days that - * certificates issued by this ACA are valid for. - */ - private Integer validDays = 1; - - /** - * Constructor. - * - * @param supplyChainValidationService the supply chain service - * @param privateKey the ACA private key - * @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 issuedCertificateRepository the issued certificate repository - * @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 policy setting repository - * @param tpm2ProvisionerStateRepository tpm2 provisioner state repository - */ - public AttestationCertificateAuthority( - final SupplyChainValidationService supplyChainValidationService, - final PrivateKey privateKey, final X509Certificate acaCertificate, - final StructConverter structConverter, - final ComponentResultRepository componentResultRepository, - final ComponentInfoRepository componentInfoRepository, - final CertificateRepository certificateRepository, - final IssuedCertificateRepository issuedCertificateRepository, - final ReferenceManifestRepository referenceManifestRepository, - final int validDays, - final DeviceRepository deviceRepository, - final ReferenceDigestValueRepository referenceDigestValueRepository, - final PolicyRepository policyRepository, - final TPM2ProvisionerStateRepository tpm2ProvisionerStateRepository) { - this.supplyChainValidationService = supplyChainValidationService; - this.privateKey = privateKey; - this.acaCertificate = acaCertificate; - this.structConverter = structConverter; - this.componentResultRepository = componentResultRepository; - this.componentInfoRepository = componentInfoRepository; - this.certificateRepository = certificateRepository; - this.issuedCertificateRepository = issuedCertificateRepository; - this.referenceManifestRepository = referenceManifestRepository; - this.validDays = validDays; - this.deviceRepository = deviceRepository; - this.referenceDigestValueRepository = referenceDigestValueRepository; - this.policyRepository = policyRepository; - this.tpm2ProvisionerStateRepository = tpm2ProvisionerStateRepository; - - this.certificateRequestHandler = new CertificateRequestProcessor(supplyChainValidationService, - certificateRepository, deviceRepository, - privateKey, acaCertificate, validDays, tpm2ProvisionerStateRepository, policyRepository); - this.identityClaimHandler = new IdentityClaimProcessor(supplyChainValidationService, - certificateRepository, componentResultRepository, componentInfoRepository, - referenceManifestRepository, referenceDigestValueRepository, - deviceRepository, tpm2ProvisionerStateRepository, policyRepository); - } - - /** - * Processes the provided identity claim. - * - * @param identityClaim a byte array representation of the identity claim - * @return processed identity claim response - */ - public byte[] processIdentityClaimTpm2(final byte[] identityClaim) { - return this.identityClaimHandler.processIdentityClaimTpm2(identityClaim); - } - - /** - * Processes the provided certificate request. - * - * @param certificateRequest a byte array representation of the certificate request - * @return processed certificate request response - */ - public byte[] processCertificateRequest(final byte[] certificateRequest) throws GeneralSecurityException { - return this.certificateRequestHandler.processCertificateRequest(certificateRequest); - } - - /** - * Retrieves the encoded public key. - * - * @return encoded public key - */ - public byte[] getPublicKey() { - return acaCertificate.getPublicKey().getEncoded(); - } -} diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/AttestationCertificateAuthorityRestController.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/AttestationCertificateAuthorityRestController.java new file mode 100644 index 000000000..b97608fa8 --- /dev/null +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/AttestationCertificateAuthorityRestController.java @@ -0,0 +1,72 @@ +package hirs.attestationca.persist; + +import lombok.extern.log4j.Log4j2; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; + +import java.security.GeneralSecurityException; + +/** + * REST Controller for the ACA that communicates with the Provisioner during the provisioning process. + */ +@Log4j2 +@RestController +@RequestMapping("/HIRS_AttestationCA") +public class AttestationCertificateAuthorityRestController { + private final AttestationCertificateAuthorityService attestationCertificateAuthorityService; + + /** + * Constructor. + * + * @param attestationCertificateAuthorityService Attestation Certificate Authority service + */ + @Autowired + public AttestationCertificateAuthorityRestController( + final AttestationCertificateAuthorityService attestationCertificateAuthorityService) { + this.attestationCertificateAuthorityService = attestationCertificateAuthorityService; + } + + /** + * Processes a given IdentityClaim and generates a response containing an encrypted nonce to be returned by the + * client in a future handshake request. + * + * @param identityClaim The request object from the provisioner. + * @return The response to the provisioner. + */ + @ResponseBody + @PostMapping(value = "/identity-claim-tpm2/process", consumes = MediaType.APPLICATION_OCTET_STREAM_VALUE) + public byte[] processIdentityClaimTpm2(@RequestBody final byte[] identityClaim) { + return this.attestationCertificateAuthorityService.processIdentityClaimTpm2(identityClaim); + } + + /** + * Processes a given Certificate Request and generates a response containing the signed, public certificate for + * the client's desired attestation key, if the correct nonce is supplied. + * + * @param certificateRequest request containing nonce from earlier identity + * claim handshake + * @return The response to the client provisioner. + */ + @ResponseBody + @PostMapping(value = "/request-certificate-tpm2", consumes = MediaType.APPLICATION_OCTET_STREAM_VALUE) + public byte[] processCertificateRequest(@RequestBody final byte[] certificateRequest) throws GeneralSecurityException { + return this.attestationCertificateAuthorityService.processCertificateRequest(certificateRequest); + } + + /** + * Processes a GET request to retrieve the byte array representation of the public key. + * + * @return byte array representation of the public key + */ + @ResponseBody + @GetMapping("/public-key") + public byte[] getLeafACACertPublicKey() { + return this.attestationCertificateAuthorityService.getLeafACACertPublicKey(); + } +} diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/AttestationCertificateAuthorityService.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/AttestationCertificateAuthorityService.java new file mode 100644 index 000000000..eaa7fbe4c --- /dev/null +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/AttestationCertificateAuthorityService.java @@ -0,0 +1,60 @@ +package hirs.attestationca.persist; + +import hirs.attestationca.persist.provision.CertificateRequestProcessor; +import hirs.attestationca.persist.provision.IdentityClaimProcessor; +import lombok.extern.log4j.Log4j2; +import org.springframework.stereotype.Service; + +import java.security.GeneralSecurityException; + +/** + * Service layer class responsible for handling both certificate and identity claim requests made by the provisioner. + */ +@Service +@Log4j2 +public class AttestationCertificateAuthorityService implements AttestationCertificateAuthorityServiceInterface { + private final CertificateRequestProcessor certificateRequestProcessor; + private final IdentityClaimProcessor identityClaimProcessor; + + /** + * Constructor. + * + * @param certificateRequestProcessor certificate request processor service + * @param identityClaimProcessor identity claim processor service + */ + public AttestationCertificateAuthorityService( + final CertificateRequestProcessor certificateRequestProcessor, + final IdentityClaimProcessor identityClaimProcessor) { + this.certificateRequestProcessor = certificateRequestProcessor; + this.identityClaimProcessor = identityClaimProcessor; + } + + /** + * Processes the provided identity claim. + * + * @param identityClaim a byte array representation of the identity claim + * @return processed identity claim response + */ + public byte[] processIdentityClaimTpm2(final byte[] identityClaim) { + return this.identityClaimProcessor.processIdentityClaimTpm2(identityClaim); + } + + /** + * Processes the provided certificate request. + * + * @param certificateRequest a byte array representation of the certificate request + * @return processed certificate request response + */ + public byte[] processCertificateRequest(final byte[] certificateRequest) throws GeneralSecurityException { + return this.certificateRequestProcessor.processCertificateRequest(certificateRequest); + } + + /** + * Retrieves the encoded public key. + * + * @return encoded public key + */ + public byte[] getLeafACACertPublicKey() { + return this.certificateRequestProcessor.getLeafACACertificatePublicKey(); + } +} diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/RestfulInterface.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/AttestationCertificateAuthorityServiceInterface.java similarity index 81% rename from HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/RestfulInterface.java rename to HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/AttestationCertificateAuthorityServiceInterface.java index 0c8bf7766..7e4520d4a 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/RestfulInterface.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/AttestationCertificateAuthorityServiceInterface.java @@ -3,9 +3,9 @@ import java.security.GeneralSecurityException; /** - * Defines the responsibilities of the Attestation Certificate Authority. + * Interface that defines the responsibilities of the Attestation Certificate Authority service. */ -public interface RestfulInterface { +public interface AttestationCertificateAuthorityServiceInterface { /** * Processes the provided identity claim. diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/RestfulAttestationCertificateAuthority.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/RestfulAttestationCertificateAuthority.java deleted file mode 100644 index 5bafc5b92..000000000 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/RestfulAttestationCertificateAuthority.java +++ /dev/null @@ -1,137 +0,0 @@ -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; -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.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.Qualifier; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.PropertySource; -import org.springframework.context.annotation.PropertySources; -import org.springframework.http.MediaType; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.ResponseBody; -import org.springframework.web.bind.annotation.RestController; - -import java.security.GeneralSecurityException; -import java.security.PrivateKey; -import java.security.cert.X509Certificate; - -/** - * Restful implementation of the {@link AttestationCertificateAuthority}. - * Exposes the ACA methods as REST endpoints. - */ -@PropertySources({ - // detects if file exists, if not, ignore errors - @PropertySource(value = "file:/etc/hirs/aca/application.properties", - ignoreResourceNotFound = true), - @PropertySource(value = "file:C:/ProgramData/hirs/aca/application.win.properties", - ignoreResourceNotFound = true) -}) -@Log4j2 -@RestController -@RequestMapping("/HIRS_AttestationCA") -public class RestfulAttestationCertificateAuthority extends AttestationCertificateAuthority { - /** - * Constructor. - * - * @param supplyChainValidationService scp service - * @param privateKey the ACA private key - * @param acaCertificate the ACA certificate - * @param structConverter the struct converter - * @param componentResultRepository the component result repository - * @param componentInfoRepository the component info repository - * @param certificateRepository the certificate manager - * @param issuedCertificateRepository the issued certificate repository - * @param referenceManifestRepository the referenceManifestManager - * @param validDays the number of days issued certs are valid - * @param deviceRepository the device manager - * @param referenceDigestValueRepository the reference event repository - * @param policyRepository the provisioning policy entity - * @param tpm2ProvisionerStateRepository the provisioner state - */ - @Autowired - public RestfulAttestationCertificateAuthority( - final SupplyChainValidationService supplyChainValidationService, - final PrivateKey privateKey, - @Qualifier("leafACACert") final X509Certificate acaCertificate, - final StructConverter structConverter, - final ComponentResultRepository componentResultRepository, - final ComponentInfoRepository componentInfoRepository, - final CertificateRepository certificateRepository, - final IssuedCertificateRepository issuedCertificateRepository, - final ReferenceManifestRepository referenceManifestRepository, - final DeviceRepository deviceRepository, - final ReferenceDigestValueRepository referenceDigestValueRepository, - @Value("${aca.certificates.validity}") final int validDays, - final PolicyRepository policyRepository, - final TPM2ProvisionerStateRepository tpm2ProvisionerStateRepository) { - super(supplyChainValidationService, privateKey, acaCertificate, structConverter, - componentResultRepository, componentInfoRepository, - certificateRepository, issuedCertificateRepository, - referenceManifestRepository, - validDays, deviceRepository, - referenceDigestValueRepository, policyRepository, tpm2ProvisionerStateRepository); - } - - /** - * Listener for identity requests from TPM 2.0 provisioning. - *

- * Processes a given IdentityClaim and generates a response - * containing an encrypted nonce to be returned by the client in - * a future handshake request. - * - * @param identityClaim The request object from the provisioner. - * @return The response to the provisioner. - */ - @Override - @ResponseBody - @PostMapping(value = "/identity-claim-tpm2/process", - consumes = MediaType.APPLICATION_OCTET_STREAM_VALUE) - public byte[] processIdentityClaimTpm2(@RequestBody final byte[] identityClaim) { - return super.processIdentityClaimTpm2(identityClaim); - } - - /** - * Processes a given CertificateRequest - * and generates a response containing the signed, public certificate for - * the client's desired attestation key, if the correct nonce is supplied. - * - * @param certificateRequest request containing nonce from earlier identity - * claim handshake - * @return The response to the client provisioner. - */ - @Override - @ResponseBody - @PostMapping(value = "/request-certificate-tpm2", - consumes = MediaType.APPLICATION_OCTET_STREAM_VALUE) - public byte[] processCertificateRequest(@RequestBody final byte[] certificateRequest) throws GeneralSecurityException { - return super.processCertificateRequest(certificateRequest); - } - - /** - * (non-javadoc) - *

- * Wrap the {@link AttestationCertificateAuthority#getPublicKey()} with a Spring - * {@link org.springframework.web.bind.annotation.RequestMapping} such that Spring can serialize - * the certificate to be returned to an HTTP Request. - */ - @Override - @ResponseBody - @GetMapping("/public-key") - public byte[] getPublicKey() { - return super.getPublicKey(); - } -} diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/manager/SupplyChainValidationRepository.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/manager/SupplyChainValidationRepository.java deleted file mode 100644 index b7571b423..000000000 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/manager/SupplyChainValidationRepository.java +++ /dev/null @@ -1,28 +0,0 @@ -package hirs.attestationca.persist.entity.manager; - -import hirs.attestationca.persist.entity.userdefined.SupplyChainValidation; -import hirs.attestationca.persist.enums.AppraisalStatus; -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.stereotype.Repository; - -import java.util.List; -import java.util.UUID; - -@Repository -public interface SupplyChainValidationRepository extends JpaRepository { - /** - * Query that retrieves a list of supply chain validation using the provided validate type. - * - * @param validationType string representation of the validate type - * @return a list of supply chain validation - */ - List findByValidationType(SupplyChainValidation.ValidationType validationType); - - /** - * Query that retrieves a list of supply chain validation using the provided validation result. - * - * @param validationResult string representation of the validation result - * @return a list of supply chain validation - */ - List findByValidationResult(AppraisalStatus.Status validationResult); -} diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/manager/SupplyChainValidationSummaryRepository.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/manager/SupplyChainValidationSummaryRepository.java index bf4964b3e..a3c68c020 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/manager/SupplyChainValidationSummaryRepository.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/manager/SupplyChainValidationSummaryRepository.java @@ -11,8 +11,7 @@ import java.util.UUID; @Repository -public interface SupplyChainValidationSummaryRepository - extends JpaRepository { +public interface SupplyChainValidationSummaryRepository extends JpaRepository { /** * Query that retrieves a supply chain validation summary using the provided device. diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/enums/PublicKeyAlgorithm.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/enums/PublicKeyAlgorithm.java index 1842b61b7..2f7b17389 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/enums/PublicKeyAlgorithm.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/enums/PublicKeyAlgorithm.java @@ -14,7 +14,7 @@ * This enum currently includes: *

*

* Each enum constant also holds its corresponding JCA (Java Cryptography Architecture) @@ -25,10 +25,25 @@ @ToString public enum PublicKeyAlgorithm { RSA("RSA", RSAPublicKey.class), - EC("EC", ECPublicKey.class), + ECC("ECC", ECPublicKey.class), UNKNOWN("", null); private final String algorithmName; private final Class keyClass; + + /** + * Converts the provided string public key algorithm into an ENUM. + * + * @param algorithmAsString public key algorithm name as a string + * @return ENUM representation of the public key algorithm + */ + public static PublicKeyAlgorithm fromString(final String algorithmAsString) { + for (PublicKeyAlgorithm algorithmEnum : PublicKeyAlgorithm.values()) { + if (algorithmEnum.getAlgorithmName().equalsIgnoreCase(algorithmAsString)) { + return algorithmEnum; + } + } + return UNKNOWN; // Return UNKNOWN if no match is found + } } diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/exceptions/CertificateProcessingException.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/exceptions/CertificateProcessingException.java index cab0c0911..6389e4be3 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/exceptions/CertificateProcessingException.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/exceptions/CertificateProcessingException.java @@ -1,7 +1,9 @@ package hirs.attestationca.persist.exceptions; +import hirs.attestationca.persist.AttestationCertificateAuthorityService; + /** - * Generic exception thrown while a {@link hirs.attestationca.persist.AttestationCertificateAuthority} + * Generic exception thrown while a {@link AttestationCertificateAuthorityService} * is processing a newly created Attestation Certificate for a validated identity. */ public class CertificateProcessingException extends RuntimeException { diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/exceptions/IdentityProcessingException.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/exceptions/IdentityProcessingException.java index 165784739..ebca3311a 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/exceptions/IdentityProcessingException.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/exceptions/IdentityProcessingException.java @@ -1,7 +1,9 @@ package hirs.attestationca.persist.exceptions; +import hirs.attestationca.persist.AttestationCertificateAuthorityService; + /** - * Generic exception thrown while a {@link hirs.attestationca.persist.AttestationCertificateAuthority} + * Generic exception thrown while a {@link AttestationCertificateAuthorityService} * is processing a newly submitted Identity. */ public class IdentityProcessingException extends RuntimeException { diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/exceptions/UnexpectedServerException.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/exceptions/UnexpectedServerException.java index ed3da171d..df4eb2668 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/exceptions/UnexpectedServerException.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/exceptions/UnexpectedServerException.java @@ -1,7 +1,9 @@ package hirs.attestationca.persist.exceptions; +import hirs.attestationca.persist.AttestationCertificateAuthorityService; + /** - * Generic exception thrown when a {@link hirs.attestationca.persist.AttestationCertificateAuthority} + * Generic exception thrown when a {@link AttestationCertificateAuthorityService} * encounters an unexpected condition that can't be handled. */ public class UnexpectedServerException extends RuntimeException { 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 f51c53ae5..309a299d3 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 @@ -16,11 +16,16 @@ import hirs.attestationca.persist.entity.userdefined.info.TPMInfo; import hirs.attestationca.persist.entity.userdefined.report.DeviceInfoReport; import hirs.attestationca.persist.enums.AppraisalStatus; +import hirs.attestationca.persist.enums.PublicKeyAlgorithm; import hirs.attestationca.persist.exceptions.CertificateProcessingException; import hirs.attestationca.persist.provision.helper.ProvisionUtils; import hirs.attestationca.persist.service.SupplyChainValidationService; import lombok.extern.log4j.Log4j2; import org.apache.commons.lang3.ArrayUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; import java.nio.charset.StandardCharsets; import java.security.GeneralSecurityException; @@ -29,6 +34,7 @@ import java.security.cert.X509Certificate; import java.util.List; +@Service @Log4j2 public class CertificateRequestProcessor extends AbstractProcessor { @@ -37,6 +43,7 @@ public class CertificateRequestProcessor extends AbstractProcessor { private final DeviceRepository deviceRepository; private final X509Certificate acaCertificate; private final TPM2ProvisionerStateRepository tpm2ProvisionerStateRepository; + private final PublicKeyAlgorithm publicKeyAlgorithm; /** * Constructor. @@ -50,12 +57,14 @@ public class CertificateRequestProcessor extends AbstractProcessor { * @param tpm2ProvisionerStateRepository db connector for provisioner state. * @param policyRepository db connector for policies. */ + @Autowired public CertificateRequestProcessor(final SupplyChainValidationService supplyChainValidationService, final CertificateRepository certificateRepository, final DeviceRepository deviceRepository, final PrivateKey privateKey, - final X509Certificate acaCertificate, - final int validDays, + @Qualifier("leafACACert") final X509Certificate acaCertificate, + @Value("${aca.current.public.key.algorithm}") final String publicKeyAlgorithmStr, + @Value("${aca.certificates.validity}") final int validDays, final TPM2ProvisionerStateRepository tpm2ProvisionerStateRepository, final PolicyRepository policyRepository) { super(privateKey, validDays); @@ -64,9 +73,19 @@ public CertificateRequestProcessor(final SupplyChainValidationService supplyChai this.deviceRepository = deviceRepository; this.acaCertificate = acaCertificate; this.tpm2ProvisionerStateRepository = tpm2ProvisionerStateRepository; + this.publicKeyAlgorithm = PublicKeyAlgorithm.fromString(publicKeyAlgorithmStr); setPolicyRepository(policyRepository); } + /** + * Retrieves the byte array representation of the ACA certificate public key. + * + * @return byte array representation of the ACA certificate public key + */ + public byte[] getLeafACACertificatePublicKey() { + return acaCertificate.getPublicKey().getEncoded(); + } + /** * Basic implementation of the ACA processCertificateRequest method. * Parses the nonce, validates its correctness, generates the signed, @@ -114,10 +133,12 @@ public byte[] processCertificateRequest(final byte[] certificateRequest) throws ProvisionerTpm2.IdentityClaim claim = ProvisionUtils.parseIdentityClaim(identityClaim); // Get endorsement public key - PublicKey ekPub = ProvisionUtils.parsePublicKeyFromPublicDataSegment(claim.getEkPublicArea().toByteArray()); + PublicKey ekPub = ProvisionUtils.parsePublicKeyFromPublicDataSegment(publicKeyAlgorithm, + claim.getEkPublicArea().toByteArray()); // Get attestation public key - PublicKey akPub = ProvisionUtils.parsePublicKeyFromPublicDataSegment(claim.getAkPublicArea().toByteArray()); + PublicKey akPub = ProvisionUtils.parsePublicKeyFromPublicDataSegment(publicKeyAlgorithm, + claim.getAkPublicArea().toByteArray()); // Get Endorsement Credential if it exists or was uploaded EndorsementCredential endorsementCredential = parseEcFromIdentityClaim(claim, ekPub, certificateRepository); @@ -129,7 +150,8 @@ public byte[] processCertificateRequest(final byte[] certificateRequest) throws // Get LDevID public key if it exists PublicKey ldevidPub = null; if (claim.hasLdevidPublicArea()) { - ldevidPub = ProvisionUtils.parsePublicKeyFromPublicDataSegment(claim.getLdevidPublicArea().toByteArray()); + ldevidPub = ProvisionUtils.parsePublicKeyFromPublicDataSegment(publicKeyAlgorithm, + claim.getLdevidPublicArea().toByteArray()); } // Get device name and device diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/IdentityClaimProcessor.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/IdentityClaimProcessor.java index 8252ce2d7..0363057ac 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/IdentityClaimProcessor.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/IdentityClaimProcessor.java @@ -48,6 +48,8 @@ import lombok.extern.log4j.Log4j2; import org.apache.commons.codec.binary.Hex; import org.apache.commons.lang3.ArrayUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; import java.io.IOException; import java.net.InetAddress; @@ -69,6 +71,7 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; +@Service @Log4j2 public class IdentityClaimProcessor extends AbstractProcessor { /** @@ -102,6 +105,7 @@ public class IdentityClaimProcessor extends AbstractProcessor { * @param tpm2ProvisionerStateRepository tpm2 provisioner state repository * @param policyRepository policy repository */ + @Autowired public IdentityClaimProcessor( final SupplyChainValidationService supplyChainValidationService, final CertificateRepository certificateRepository, diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/helper/ProvisionUtils.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/helper/ProvisionUtils.java index 4ff13866b..7cc8081d4 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/helper/ProvisionUtils.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/helper/ProvisionUtils.java @@ -48,7 +48,6 @@ import java.security.spec.InvalidKeySpecException; import java.security.spec.MGF1ParameterSpec; import java.security.spec.RSAPublicKeySpec; -import java.security.spec.X509EncodedKeySpec; import java.util.Date; @Log4j2 @@ -168,16 +167,16 @@ public static String getPemEncodedCertificate(final X509Certificate certificate) * Parses a public key from a byte array and returns it as a generic PublicKey. * Supports RSA and EC (Elliptic Curve) keys. * - * @param keyBytes the DER-encoded public key bytes + * @param publicKeyAlgorithm public key algorithm + * @param keyBytes the DER-encoded public key bytes * @return the parsed PublicKey instance * @throws GeneralSecurityException if the key cannot be parsed */ - public static PublicKey parsePublicKeyFromPublicDataSegment(byte[] keyBytes) throws GeneralSecurityException { - PublicKeyAlgorithm publicKeyAlgorithm = determineKeyType(keyBytes); - + public static PublicKey parsePublicKeyFromPublicDataSegment(final PublicKeyAlgorithm publicKeyAlgorithm, + byte[] keyBytes) throws GeneralSecurityException { return switch (publicKeyAlgorithm) { case RSA -> parseRSAKeyFromPublicDataSegment(keyBytes); - case EC -> parseECKeyFromPublicDataSegment(keyBytes); + case ECC -> parseECCKeyFromPublicDataSegment(keyBytes); default -> throw new GeneralSecurityException("Unsupported or invalid public key"); }; } @@ -247,7 +246,7 @@ public static PublicKey assembleRSAPublicKey(final BigInteger modulus) { * @param publicArea the public area segment to parse * @return the ECC public key of the supplied public data */ - public static ECPublicKey parseECKeyFromPublicDataSegment(final byte[] publicArea) { + public static ECPublicKey parseECCKeyFromPublicDataSegment(final byte[] publicArea) { return null; } @@ -318,7 +317,8 @@ public static byte[] decryptSymmetricBlob(final byte[] symmetricBlob, final byte } catch (IllegalBlockSizeException | InvalidKeyException | NoSuchAlgorithmException | BadPaddingException | NoSuchPaddingException | InvalidAlgorithmParameterException exception) { - log.error("Encountered error while decrypting symmetric blob of an identity request: {}", exception.getMessage(), exception); + log.error("Encountered error while decrypting symmetric blob of an identity request: {}", + exception.getMessage(), exception); } return new byte[0]; @@ -740,32 +740,4 @@ public static int daysBetween(final Date date1, final Date date2) { return (int) ((date2.getTime() - date1.getTime()) / (millisecondsInASecond * secondsInAnHour * hoursInADay)); } - - /** - * Helper method that determines the public key algorithm associated with the supplied byte array. - * - * @param keyBytes byte array representation of the key - * @return {@link PublicKeyAlgorithm} - */ - private static PublicKeyAlgorithm determineKeyType(byte[] keyBytes) { - // Loop over supported algorithms - for (PublicKeyAlgorithm publicKeyAlgorithm : PublicKeyAlgorithm.values()) { - if (publicKeyAlgorithm == PublicKeyAlgorithm.UNKNOWN) continue; // Skip UNKNOWN - - try { - KeyFactory keyFactory = KeyFactory.getInstance(publicKeyAlgorithm.getAlgorithmName()); - X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes); - PublicKey publicKey = keyFactory.generatePublic(keySpec); - - // Check if the parsed key matches the expected type - if (publicKeyAlgorithm.getKeyClass().isInstance(publicKey)) { - return publicKeyAlgorithm; - } - } catch (Exception ignored) { - // Ignore and try next algorithm - } - } - - return PublicKeyAlgorithm.UNKNOWN; - } } diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/service/SupplyChainValidationService.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/service/SupplyChainValidationService.java index c6ef4c6d6..943461db3 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/service/SupplyChainValidationService.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/service/SupplyChainValidationService.java @@ -9,7 +9,6 @@ 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.SupplyChainValidationRepository; import hirs.attestationca.persist.entity.manager.SupplyChainValidationSummaryRepository; import hirs.attestationca.persist.entity.userdefined.Device; import hirs.attestationca.persist.entity.userdefined.PolicySettings; @@ -45,8 +44,8 @@ import static hirs.attestationca.persist.enums.AppraisalStatus.Status.FAIL; import static hirs.attestationca.persist.enums.AppraisalStatus.Status.PASS; -@Log4j2 @Service +@Log4j2 public class SupplyChainValidationService { private final CACredentialRepository caCredentialRepository; @@ -56,7 +55,6 @@ public class SupplyChainValidationService { private final ComponentResultRepository componentResultRepository; private final ComponentAttributeRepository componentAttributeRepository; private final CertificateRepository certificateRepository; - private final SupplyChainValidationRepository supplyChainValidationRepository; private final SupplyChainValidationSummaryRepository supplyChainValidationSummaryRepository; private UUID provisionSessionId; @@ -69,7 +67,6 @@ public class SupplyChainValidationService { * @param componentResultRepository the comp result manager * @param componentAttributeRepository component attribute repository * @param referenceManifestRepository the RIM manager - * @param supplyChainValidationRepository the scv manager * @param supplyChainValidationSummaryRepository the summary manager * @param referenceDigestValueRepository the even manager */ @@ -81,7 +78,6 @@ public SupplyChainValidationService( final ComponentResultRepository componentResultRepository, final ComponentAttributeRepository componentAttributeRepository, final ReferenceManifestRepository referenceManifestRepository, - final SupplyChainValidationRepository supplyChainValidationRepository, final SupplyChainValidationSummaryRepository supplyChainValidationSummaryRepository, final ReferenceDigestValueRepository referenceDigestValueRepository) { this.caCredentialRepository = caCredentialRepository; @@ -90,7 +86,6 @@ public SupplyChainValidationService( this.componentResultRepository = componentResultRepository; this.componentAttributeRepository = componentAttributeRepository; this.referenceManifestRepository = referenceManifestRepository; - this.supplyChainValidationRepository = supplyChainValidationRepository; this.supplyChainValidationSummaryRepository = supplyChainValidationSummaryRepository; this.referenceDigestValueRepository = referenceDigestValueRepository; } diff --git a/HIRS_AttestationCA/src/test/java/hirs/attestationca/persist/AttestationCertificateAuthorityTest.java b/HIRS_AttestationCA/src/test/java/hirs/attestationca/persist/AttestationCertificateAuthorityTest.java index 2ff41e57f..c9721002c 100644 --- a/HIRS_AttestationCA/src/test/java/hirs/attestationca/persist/AttestationCertificateAuthorityTest.java +++ b/HIRS_AttestationCA/src/test/java/hirs/attestationca/persist/AttestationCertificateAuthorityTest.java @@ -73,7 +73,7 @@ import static org.mockito.Mockito.when; /** - * Test suite for {@link AttestationCertificateAuthority}. + * Test suite for {@link AttestationCertificateAuthorityService}. */ @TestInstance(TestInstance.Lifecycle.PER_CLASS) // needed to use non-static BeforeAll public class AttestationCertificateAuthorityTest { @@ -123,7 +123,7 @@ public class AttestationCertificateAuthorityTest { + "e2 94"; private final Random random = new Random(); // object in test - private AttestationCertificateAuthority aca; + private AttestationCertificateAuthorityService aca; private AccessAbstractProcessor abstractProcessor; // test key pair private KeyPair keyPair; @@ -174,9 +174,8 @@ public void setupTests() throws Exception { keyPair = keyPairGenerator.generateKeyPair(); //BeforeTest - aca = new AttestationCertificateAuthority(null, keyPair.getPrivate(), - null, null, null, null, null, null, null, 1, - null, null, null, null) { + aca = new AttestationCertificateAuthorityService(null, + null) { }; abstractProcessor = new AccessAbstractProcessor(keyPair.getPrivate(), 1); @@ -184,7 +183,7 @@ public void setupTests() throws Exception { } /** - * Tests {@link AttestationCertificateAuthority#processIdentityClaimTpm2(byte[])} + * Tests {@link AttestationCertificateAuthorityService#processIdentityClaimTpm2(byte[])} * where the byte array is null. Expects an illegal argument exception to be thrown. */ @Test @@ -194,7 +193,7 @@ public void testProcessIdentityClaimTpm2NullRequest() { } /** - * Tests {@link AttestationCertificateAuthority#getPublicKey()}. + * Tests {@link AttestationCertificateAuthorityService#getLeafACACertPublicKey()}. */ @Test public void testGetPublicKey() { @@ -216,7 +215,7 @@ public void testGetPublicKey() { when(publicKey.getEncoded()).thenReturn(encoded); // assert what the ACA returns is as expected - assertArrayEquals(encoded, aca.getPublicKey()); + assertArrayEquals(encoded, aca.getLeafACACertPublicKey()); // verify mock interactions verify(acaCertificate).getPublicKey(); @@ -413,7 +412,7 @@ public void testGenerateAttestation() throws Exception { } /** - * Tests {@link AttestationCertificateAuthority# + * Tests {@link AttestationCertificateAuthorityService# * AttestationCertificateAuthority(SupplyChainValidationService, PrivateKey, * X509Certificate, StructConverter, CertificateManager, DeviceRegister, int, * DeviceManager, DBManager)}. diff --git a/HIRS_AttestationCAPortal/src/main/resources/application.properties b/HIRS_AttestationCAPortal/src/main/resources/application.properties index 0a5040c45..f75e7b107 100644 --- a/HIRS_AttestationCAPortal/src/main/resources/application.properties +++ b/HIRS_AttestationCAPortal/src/main/resources/application.properties @@ -33,6 +33,7 @@ aca.certificates.leaf-three-key-alias=HIRS_leaf_ca3_rsa_3k_sha384 aca.certificates.intermediate-key-alias=HIRS_intermediate_ca_rsa_3k_sha384 aca.certificates.root-key-alias=HIRS_root_ca_rsa_3k_sha384 aca.certificates.validity=3652 +aca.current.public.key.algorithm=rsa # Compression settings server.compression.enabled=true # Compression content types diff --git a/HIRS_AttestationCAPortal/src/main/resources/application.win.properties b/HIRS_AttestationCAPortal/src/main/resources/application.win.properties index 946fb2d2f..7d39de846 100644 --- a/HIRS_AttestationCAPortal/src/main/resources/application.win.properties +++ b/HIRS_AttestationCAPortal/src/main/resources/application.win.properties @@ -39,6 +39,7 @@ aca.certificates.leaf-three-key-alias=HIRS_leaf_ca3_rsa_3k_sha384 aca.certificates.intermediate-key-alias=HIRS_intermediate_ca_rsa_3k_sha384 aca.certificates.root-key-alias=HIRS_root_ca_rsa_3k_sha384 aca.certificates.validity=3652 +aca.current.public.key.algorithm=rsa # Compression settings server.compression.enabled=true # Compression content types diff --git a/package/linux/aca/aca_setup.sh b/package/linux/aca/aca_setup.sh index cb87799ad..399035847 100755 --- a/package/linux/aca/aca_setup.sh +++ b/package/linux/aca/aca_setup.sh @@ -218,15 +218,18 @@ fi sed -i '/aca.certificates.leaf-three-key-alias/d' $SPRING_PROP_FILE sed -i '/aca.certificates.intermediate-key-alias/d' $SPRING_PROP_FILE sed -i '/aca.certificates.root-key-alias/d' $SPRING_PROP_FILE + sed -i '/aca.current.public.key.algorithm/d' $SPRING_PROP_FILE if [ "$ACA_ALG" == "rsa" ]; then # Add new lines for aca aliases echo "aca.certificates.leaf-three-key-alias=HIRS_leaf_ca3_rsa_3k_sha384_key" >> $SPRING_PROP_FILE echo "aca.certificates.intermediate-key-alias=HIRS_intermediate_ca_rsa_3k_sha384_key" >> $SPRING_PROP_FILE echo "aca.certificates.root-key-alias=HIRS_root_ca_rsa_3k_sha384_key" >> $SPRING_PROP_FILE + echo "aca.current.public.key.algorithm=rsa" >> $SPRING_PROP_FILE elif [ "$ACA_ALG" == "ecc" ]; then echo "aca.certificates.leaf-three-key-alias=HIRS_leaf_ca3_ecc_512_sha384_key" >> $SPRING_PROP_FILE echo "aca.certificates.intermediate-key-alias=HIRS_intermediate_ca_ecc_512_sha384_key" >> $SPRING_PROP_FILE echo "aca.certificates.root-key-alias=HIRS_root_ca_ecc_512_sha384_key" >> $SPRING_PROP_FILE + echo "aca.current.public.key.algorithm=ecc" >> $SPRING_PROP_FILE fi echo "ACA setup complete" | tee -a "$LOG_FILE" From 879ec7e2598ab60380778972fbdaa045d9cb673c Mon Sep 17 00:00:00 2001 From: ThatSilentCoder <184309164+ThatSilentCoder@users.noreply.github.com> Date: Fri, 20 Feb 2026 17:41:15 -0500 Subject: [PATCH 03/25] v3.1_issue_1090: Figuring out how to parse the ecc key from the bytes array. Fixing up some warnings for the shell scripts. --- .ci/system-tests/sys_test_common.sh | 8 +-- ...ionCertificateAuthorityRestController.java | 19 +++--- ...ttestationCertificateAuthorityService.java | 46 ++----------- ...tationCertificateAuthorityServiceImpl.java | 60 +++++++++++++++++ ...nCertificateAuthorityServiceInterface.java | 26 -------- .../persist/enums/PublicKeyAlgorithm.java | 2 +- .../CertificateProcessingException.java | 4 +- .../IdentityProcessingException.java | 4 +- .../exceptions/UnexpectedServerException.java | 4 +- .../provision/IdentityClaimProcessor.java | 8 ++- .../provision/helper/ProvisionUtils.java | 64 ++++++++++++------- .../AttestationCertificateAuthorityTest.java | 12 ++-- package/linux/aca/aca_check_setup.sh | 35 +++++----- package/linux/aca/aca_remove_setup.sh | 10 +-- package/linux/aca/aca_setup.sh | 50 +++++++++------ 15 files changed, 191 insertions(+), 161 deletions(-) create mode 100644 HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/AttestationCertificateAuthorityServiceImpl.java delete mode 100644 HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/AttestationCertificateAuthorityServiceInterface.java diff --git a/.ci/system-tests/sys_test_common.sh b/.ci/system-tests/sys_test_common.sh index a31313432..a7791eff5 100644 --- a/.ci/system-tests/sys_test_common.sh +++ b/.ci/system-tests/sys_test_common.sh @@ -13,11 +13,11 @@ tpm2_container=hirs-provisioner1-tpm2 checkContainerStatus() { container_name=$1 container_id="$(docker ps -aqf "name=$container_name")" - container_status="$(docker inspect $container_id --format='{{.State.Status}}')" + container_status="$(docker inspect "$container_id" --format='{{.State.Status}}')" echo "Container id is $container_id and the status is $container_status" if [ "$container_status" != "running" ]; then - container_exit_code="$(docker inspect $container_id --format='{{.State.ExitCode}}')" + container_exit_code="$(docker inspect "$container_id" --format='{{.State.ExitCode}}')" echo "Container Exit Code: $container_exit_code" docker info exit 1; @@ -61,7 +61,7 @@ clearAcaDb() { docker exec -i $aca_container mysql -u root -proot -e "use hirs_db; set foreign_key_checks=0; truncate Appraiser; truncate Certificate;truncate Issued_Attestation_Platform_Join_Table;truncate CertificatesUsedToValidate;truncate ComponentAttributeResult; truncate ComponentInfo;truncate ComponentResult;truncate Device;truncate DeviceInfoReport;truncate PortalInfo; - truncate ReferenceDigestValue;truncate ReferenceManifest;truncate Report;truncate SupplyChainValidation; + truncate ReferenceDigestValue;truncate ReferenceManifest;truncate Report; truncate SupplyChainValidationSummary;truncate SupplyChainValidationSummary_SupplyChainValidation; truncate TPM2ProvisionerState;set foreign_key_checks=1;" } @@ -146,6 +146,6 @@ setAppsettings() { # write_to_logs writeToLogs() { line=$1 - echo $line; + echo "$line"; docker exec -i $aca_container /bin/bash -c "cd .. && echo '$line' >> /var/log/hirs/HIRS_AttestationCA_Portal.log" } diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/AttestationCertificateAuthorityRestController.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/AttestationCertificateAuthorityRestController.java index b97608fa8..ff991839b 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/AttestationCertificateAuthorityRestController.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/AttestationCertificateAuthorityRestController.java @@ -19,17 +19,17 @@ @RestController @RequestMapping("/HIRS_AttestationCA") public class AttestationCertificateAuthorityRestController { - private final AttestationCertificateAuthorityService attestationCertificateAuthorityService; + private final AttestationCertificateAuthorityServiceImpl attestationCertificateAuthorityServiceImpl; /** * Constructor. * - * @param attestationCertificateAuthorityService Attestation Certificate Authority service + * @param attestationCertificateAuthorityServiceImpl Attestation Certificate Authority service */ @Autowired public AttestationCertificateAuthorityRestController( - final AttestationCertificateAuthorityService attestationCertificateAuthorityService) { - this.attestationCertificateAuthorityService = attestationCertificateAuthorityService; + final AttestationCertificateAuthorityServiceImpl attestationCertificateAuthorityServiceImpl) { + this.attestationCertificateAuthorityServiceImpl = attestationCertificateAuthorityServiceImpl; } /** @@ -41,8 +41,8 @@ public AttestationCertificateAuthorityRestController( */ @ResponseBody @PostMapping(value = "/identity-claim-tpm2/process", consumes = MediaType.APPLICATION_OCTET_STREAM_VALUE) - public byte[] processIdentityClaimTpm2(@RequestBody final byte[] identityClaim) { - return this.attestationCertificateAuthorityService.processIdentityClaimTpm2(identityClaim); + public byte[] processIdentityClaimTpm2(@RequestBody final byte[] identityClaim) throws GeneralSecurityException { + return this.attestationCertificateAuthorityServiceImpl.processIdentityClaimTpm2(identityClaim); } /** @@ -55,8 +55,9 @@ public byte[] processIdentityClaimTpm2(@RequestBody final byte[] identityClaim) */ @ResponseBody @PostMapping(value = "/request-certificate-tpm2", consumes = MediaType.APPLICATION_OCTET_STREAM_VALUE) - public byte[] processCertificateRequest(@RequestBody final byte[] certificateRequest) throws GeneralSecurityException { - return this.attestationCertificateAuthorityService.processCertificateRequest(certificateRequest); + public byte[] processCertificateRequest(@RequestBody final byte[] certificateRequest) + throws GeneralSecurityException { + return this.attestationCertificateAuthorityServiceImpl.processCertificateRequest(certificateRequest); } /** @@ -67,6 +68,6 @@ public byte[] processCertificateRequest(@RequestBody final byte[] certificateReq @ResponseBody @GetMapping("/public-key") public byte[] getLeafACACertPublicKey() { - return this.attestationCertificateAuthorityService.getLeafACACertPublicKey(); + return this.attestationCertificateAuthorityServiceImpl.getLeafACACertPublicKey(); } } diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/AttestationCertificateAuthorityService.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/AttestationCertificateAuthorityService.java index eaa7fbe4c..54a40c9ba 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/AttestationCertificateAuthorityService.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/AttestationCertificateAuthorityService.java @@ -1,60 +1,26 @@ package hirs.attestationca.persist; -import hirs.attestationca.persist.provision.CertificateRequestProcessor; -import hirs.attestationca.persist.provision.IdentityClaimProcessor; -import lombok.extern.log4j.Log4j2; -import org.springframework.stereotype.Service; - import java.security.GeneralSecurityException; /** - * Service layer class responsible for handling both certificate and identity claim requests made by the provisioner. + * Interface that defines the responsibilities of the Attestation Certificate Authority service. */ -@Service -@Log4j2 -public class AttestationCertificateAuthorityService implements AttestationCertificateAuthorityServiceInterface { - private final CertificateRequestProcessor certificateRequestProcessor; - private final IdentityClaimProcessor identityClaimProcessor; - - /** - * Constructor. - * - * @param certificateRequestProcessor certificate request processor service - * @param identityClaimProcessor identity claim processor service - */ - public AttestationCertificateAuthorityService( - final CertificateRequestProcessor certificateRequestProcessor, - final IdentityClaimProcessor identityClaimProcessor) { - this.certificateRequestProcessor = certificateRequestProcessor; - this.identityClaimProcessor = identityClaimProcessor; - } +public interface AttestationCertificateAuthorityService { /** * Processes the provided identity claim. * * @param identityClaim a byte array representation of the identity claim - * @return processed identity claim response + * @return a byte array representation of the identity claim response */ - public byte[] processIdentityClaimTpm2(final byte[] identityClaim) { - return this.identityClaimProcessor.processIdentityClaimTpm2(identityClaim); - } + byte[] processIdentityClaimTpm2(byte[] identityClaim) throws GeneralSecurityException; /** * Processes the provided certificate request. * * @param certificateRequest a byte array representation of the certificate request - * @return processed certificate request response + * @return a byte array representation of the certificate request response */ - public byte[] processCertificateRequest(final byte[] certificateRequest) throws GeneralSecurityException { - return this.certificateRequestProcessor.processCertificateRequest(certificateRequest); - } + byte[] processCertificateRequest(byte[] certificateRequest) throws GeneralSecurityException; - /** - * Retrieves the encoded public key. - * - * @return encoded public key - */ - public byte[] getLeafACACertPublicKey() { - return this.certificateRequestProcessor.getLeafACACertificatePublicKey(); - } } diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/AttestationCertificateAuthorityServiceImpl.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/AttestationCertificateAuthorityServiceImpl.java new file mode 100644 index 000000000..6b77a05d4 --- /dev/null +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/AttestationCertificateAuthorityServiceImpl.java @@ -0,0 +1,60 @@ +package hirs.attestationca.persist; + +import hirs.attestationca.persist.provision.CertificateRequestProcessor; +import hirs.attestationca.persist.provision.IdentityClaimProcessor; +import lombok.extern.log4j.Log4j2; +import org.springframework.stereotype.Service; + +import java.security.GeneralSecurityException; + +/** + * Service layer class responsible for handling both certificate and identity claim requests made by the provisioner. + */ +@Service +@Log4j2 +public class AttestationCertificateAuthorityServiceImpl implements AttestationCertificateAuthorityService { + private final CertificateRequestProcessor certificateRequestProcessor; + private final IdentityClaimProcessor identityClaimProcessor; + + /** + * Constructor. + * + * @param certificateRequestProcessor certificate request processor service + * @param identityClaimProcessor identity claim processor service + */ + public AttestationCertificateAuthorityServiceImpl( + final CertificateRequestProcessor certificateRequestProcessor, + final IdentityClaimProcessor identityClaimProcessor) { + this.certificateRequestProcessor = certificateRequestProcessor; + this.identityClaimProcessor = identityClaimProcessor; + } + + /** + * Processes the provided identity claim. + * + * @param identityClaim a byte array representation of the identity claim + * @return processed identity claim response + */ + public byte[] processIdentityClaimTpm2(final byte[] identityClaim) throws GeneralSecurityException { + return this.identityClaimProcessor.processIdentityClaimTpm2(identityClaim); + } + + /** + * Processes the provided certificate request. + * + * @param certificateRequest a byte array representation of the certificate request + * @return processed certificate request response + */ + public byte[] processCertificateRequest(final byte[] certificateRequest) throws GeneralSecurityException { + return this.certificateRequestProcessor.processCertificateRequest(certificateRequest); + } + + /** + * Retrieves the encoded public key. + * + * @return encoded public key + */ + public byte[] getLeafACACertPublicKey() { + return this.certificateRequestProcessor.getLeafACACertificatePublicKey(); + } +} diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/AttestationCertificateAuthorityServiceInterface.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/AttestationCertificateAuthorityServiceInterface.java deleted file mode 100644 index 7e4520d4a..000000000 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/AttestationCertificateAuthorityServiceInterface.java +++ /dev/null @@ -1,26 +0,0 @@ -package hirs.attestationca.persist; - -import java.security.GeneralSecurityException; - -/** - * Interface that defines the responsibilities of the Attestation Certificate Authority service. - */ -public interface AttestationCertificateAuthorityServiceInterface { - - /** - * Processes the provided identity claim. - * - * @param identityClaim a byte array representation of the identity claim - * @return a byte array representation of the identity claim response - */ - byte[] processIdentityClaimTpm2(byte[] identityClaim); - - /** - * Processes the provided certificate request. - * - * @param certificateRequest a byte array representation of the certificate request - * @return a byte array representation of the certificate request response - */ - byte[] processCertificateRequest(byte[] certificateRequest) throws GeneralSecurityException; - -} diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/enums/PublicKeyAlgorithm.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/enums/PublicKeyAlgorithm.java index 2f7b17389..a4a8c85ad 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/enums/PublicKeyAlgorithm.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/enums/PublicKeyAlgorithm.java @@ -26,7 +26,7 @@ public enum PublicKeyAlgorithm { RSA("RSA", RSAPublicKey.class), ECC("ECC", ECPublicKey.class), - UNKNOWN("", null); + UNKNOWN("UNKNOWN", null); private final String algorithmName; private final Class keyClass; diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/exceptions/CertificateProcessingException.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/exceptions/CertificateProcessingException.java index 6389e4be3..84d5eaa1f 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/exceptions/CertificateProcessingException.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/exceptions/CertificateProcessingException.java @@ -1,9 +1,9 @@ package hirs.attestationca.persist.exceptions; -import hirs.attestationca.persist.AttestationCertificateAuthorityService; +import hirs.attestationca.persist.AttestationCertificateAuthorityServiceImpl; /** - * Generic exception thrown while a {@link AttestationCertificateAuthorityService} + * Generic exception thrown while a {@link AttestationCertificateAuthorityServiceImpl} * is processing a newly created Attestation Certificate for a validated identity. */ public class CertificateProcessingException extends RuntimeException { diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/exceptions/IdentityProcessingException.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/exceptions/IdentityProcessingException.java index ebca3311a..5b6cc5eec 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/exceptions/IdentityProcessingException.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/exceptions/IdentityProcessingException.java @@ -1,9 +1,9 @@ package hirs.attestationca.persist.exceptions; -import hirs.attestationca.persist.AttestationCertificateAuthorityService; +import hirs.attestationca.persist.AttestationCertificateAuthorityServiceImpl; /** - * Generic exception thrown while a {@link AttestationCertificateAuthorityService} + * Generic exception thrown while a {@link AttestationCertificateAuthorityServiceImpl} * is processing a newly submitted Identity. */ public class IdentityProcessingException extends RuntimeException { diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/exceptions/UnexpectedServerException.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/exceptions/UnexpectedServerException.java index df4eb2668..890f6c2af 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/exceptions/UnexpectedServerException.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/exceptions/UnexpectedServerException.java @@ -1,9 +1,9 @@ package hirs.attestationca.persist.exceptions; -import hirs.attestationca.persist.AttestationCertificateAuthorityService; +import hirs.attestationca.persist.AttestationCertificateAuthorityServiceImpl; /** - * Generic exception thrown when a {@link AttestationCertificateAuthorityService} + * Generic exception thrown when a {@link AttestationCertificateAuthorityServiceImpl} * encounters an unexpected condition that can't be handled. */ public class UnexpectedServerException extends RuntimeException { diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/IdentityClaimProcessor.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/IdentityClaimProcessor.java index 0363057ac..89272bca6 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/IdentityClaimProcessor.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/IdentityClaimProcessor.java @@ -35,6 +35,7 @@ import hirs.attestationca.persist.entity.userdefined.rim.ReferenceDigestValue; import hirs.attestationca.persist.entity.userdefined.rim.SupportReferenceManifest; import hirs.attestationca.persist.enums.AppraisalStatus; +import hirs.attestationca.persist.enums.PublicKeyAlgorithm; import hirs.attestationca.persist.exceptions.IdentityProcessingException; import hirs.attestationca.persist.provision.helper.ProvisionUtils; import hirs.attestationca.persist.service.SupplyChainValidationService; @@ -49,6 +50,7 @@ import org.apache.commons.codec.binary.Hex; import org.apache.commons.lang3.ArrayUtils; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import java.io.IOException; @@ -78,8 +80,7 @@ public class IdentityClaimProcessor extends AbstractProcessor { * Number of bytes to include in the TPM2.0 nonce. */ public static final int NONCE_LENGTH = 20; - private static final String PCR_QUOTE_MASK = "0,1,2,3,4,5,6,7,8,9,10,11,12,13," - + "14,15,16,17,18,19,20,21,22,23"; + private static final String PCR_QUOTE_MASK = "0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23"; private static final int NUM_OF_VARIABLES = 5; private static final int MAC_BYTES = 6; @@ -91,6 +92,7 @@ public class IdentityClaimProcessor extends AbstractProcessor { private final ReferenceDigestValueRepository referenceDigestValueRepository; private final DeviceRepository deviceRepository; private final TPM2ProvisionerStateRepository tpm2ProvisionerStateRepository; + private final PublicKeyAlgorithm publicKeyAlgorithm; /** * Constructor. @@ -107,6 +109,7 @@ public class IdentityClaimProcessor extends AbstractProcessor { */ @Autowired public IdentityClaimProcessor( + @Value("${aca.current.public.key.algorithm}") final String publicKeyAlgorithmStr, final SupplyChainValidationService supplyChainValidationService, final CertificateRepository certificateRepository, final ComponentResultRepository componentResultRepository, @@ -124,6 +127,7 @@ public IdentityClaimProcessor( this.referenceDigestValueRepository = referenceDigestValueRepository; this.deviceRepository = deviceRepository; this.tpm2ProvisionerStateRepository = tpm2ProvisionerStateRepository; + this.publicKeyAlgorithm = PublicKeyAlgorithm.fromString(publicKeyAlgorithmStr); setPolicyRepository(policyRepository); } diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/helper/ProvisionUtils.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/helper/ProvisionUtils.java index 7cc8081d4..4080e2234 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/helper/ProvisionUtils.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/helper/ProvisionUtils.java @@ -10,6 +10,7 @@ import hirs.structs.converters.SimpleStructBuilder; import hirs.structs.elements.aca.SymmetricAttestation; import hirs.structs.elements.tpm.EncryptionScheme; +import hirs.structs.elements.tpm.IdentityRequest; import hirs.structs.elements.tpm.SymmetricKey; import hirs.structs.elements.tpm.SymmetricKeyParams; import hirs.utils.HexUtils; @@ -45,6 +46,7 @@ import java.security.cert.X509Certificate; import java.security.interfaces.ECPublicKey; import java.security.interfaces.RSAPublicKey; +import java.security.spec.ECPublicKeySpec; import java.security.spec.InvalidKeySpecException; import java.security.spec.MGF1ParameterSpec; import java.security.spec.RSAPublicKeySpec; @@ -91,7 +93,9 @@ public final class ProvisionUtils { private static final int TPM2_CREDENTIAL_BLOB_SIZE = 392; - private static final int RSA_MODULUS_LENGTH = 256; + private static final int DEFAULT_RSA_MODULUS_LENGTH_IN_BYTES = 256; + + private static final int DEFAULT_ECC_LENGTH = 0; // Constants used to parse out the ak name from the ak public data. Used in generateAkName private static final String AK_NAME_PREFIX = "000b"; @@ -108,11 +112,9 @@ private ProvisionUtils() { } /** - * Helper method to parse a byte array into an - * {@link hirs.attestationca.configuration.provisionerTpm2.ProvisionerTpm2.IdentityClaim}. + * Helper method to parse a byte array into an {@link ProvisionerTpm2.IdentityClaim}. * - * @param identityClaim byte array that should be converted to a Protobuf IdentityClaim - * object + * @param identityClaim byte array that should be converted to a Protobuf IdentityClaim object * @return the Protobuf generated Identity Claim object */ public static ProvisionerTpm2.IdentityClaim parseIdentityClaim(final byte[] identityClaim) { @@ -173,7 +175,7 @@ public static String getPemEncodedCertificate(final X509Certificate certificate) * @throws GeneralSecurityException if the key cannot be parsed */ public static PublicKey parsePublicKeyFromPublicDataSegment(final PublicKeyAlgorithm publicKeyAlgorithm, - byte[] keyBytes) throws GeneralSecurityException { + final byte[] keyBytes) throws GeneralSecurityException { return switch (publicKeyAlgorithm) { case RSA -> parseRSAKeyFromPublicDataSegment(keyBytes); case ECC -> parseECCKeyFromPublicDataSegment(keyBytes); @@ -189,14 +191,13 @@ public static PublicKey parsePublicKeyFromPublicDataSegment(final PublicKeyAlgor */ public static RSAPublicKey parseRSAKeyFromPublicDataSegment(final byte[] publicArea) { final int pubLen = publicArea.length; - if (pubLen < RSA_MODULUS_LENGTH) { - throw new IllegalArgumentException( - "EK or AK public data segment is not long enough"); + + if (pubLen < DEFAULT_RSA_MODULUS_LENGTH_IN_BYTES) { + throw new IllegalArgumentException("EK or AK public data segment is not long enough"); } + // public data ends with 256 byte modulus - byte[] modulus = HexUtils.subarray(publicArea, - pubLen - RSA_MODULUS_LENGTH, - pubLen - 1); + byte[] modulus = HexUtils.subarray(publicArea, pubLen - DEFAULT_RSA_MODULUS_LENGTH_IN_BYTES, pubLen - 1); return (RSAPublicKey) assembleRSAPublicKey(modulus); } @@ -227,16 +228,16 @@ public static PublicKey assembleRSAPublicKey(final String modulus) { * @return RSA public key using the provided integer modulus */ public static PublicKey assembleRSAPublicKey(final BigInteger modulus) { - // generate a key spec using mod and exp + // generate an RSA key spec using mod and exp RSAPublicKeySpec keySpec = new RSAPublicKeySpec(modulus, EXPONENT); - // create the public key + // create the RSA public key try { - KeyFactory keyFactory = KeyFactory.getInstance("RSA"); + KeyFactory keyFactory = KeyFactory.getInstance(PublicKeyAlgorithm.RSA.getAlgorithmName()); return keyFactory.generatePublic(keySpec); } catch (NoSuchAlgorithmException | InvalidKeySpecException e) { throw new UnexpectedServerException( - "Encountered unexpected error creating public key: " + e.getMessage(), e); + "Encountered unexpected error creating RSA public key: " + e.getMessage(), e); } } @@ -247,12 +248,27 @@ public static PublicKey assembleRSAPublicKey(final BigInteger modulus) { * @return the ECC public key of the supplied public data */ public static ECPublicKey parseECCKeyFromPublicDataSegment(final byte[] publicArea) { - return null; + final int pubLen = publicArea.length; + + return (ECPublicKey) assembleECCPublicKey(); + } + + public static PublicKey assembleECCPublicKey() { + ECPublicKeySpec ecPublicKeySpec = null; + + // create the RSA public key + try { + KeyFactory keyFactory = KeyFactory.getInstance(PublicKeyAlgorithm.ECC.getAlgorithmName()); + return keyFactory.generatePublic(ecPublicKeySpec); + } catch (NoSuchAlgorithmException | InvalidKeySpecException e) { + throw new UnexpectedServerException( + "Encountered unexpected error creating ECC public key: " + e.getMessage(), e); + } } /** * Will attempt to decrypt the asymmetric blob that originated from an - * {@link hirs.structs.elements.tpm.IdentityRequest} using the cipher transformation. + * {@link IdentityRequest} using the cipher transformation. * * @param asymmetricBlob to be decrypted * @param scheme to decrypt with @@ -291,7 +307,7 @@ public static byte[] decryptAsymmetricBlob(final byte[] asymmetricBlob, /** * Will attempt to decrypt the symmetric blob that originated from an - * {@link hirs.structs.elements.tpm.IdentityRequest} using the specified symmetric key + * {@link IdentityRequest} using the specified symmetric key * and cipher transformation. * * @param symmetricBlob to be decrypted @@ -379,14 +395,14 @@ public static ByteString tpm20MakeCredential(final RSAPublicKey ek, final RSAPub // generate ak name from akMod byte[] akModTemp = ak.getModulus().toByteArray(); - byte[] akMod = new byte[RSA_MODULUS_LENGTH]; + byte[] akMod = new byte[DEFAULT_RSA_MODULUS_LENGTH_IN_BYTES]; int startpos = 0; // BigIntegers are signed, so a modulus that has a first bit of 1 // will be padded with a zero byte that must be removed if (akModTemp[0] == 0x00) { startpos = 1; } - System.arraycopy(akModTemp, startpos, akMod, 0, RSA_MODULUS_LENGTH); + System.arraycopy(akModTemp, startpos, akMod, 0, DEFAULT_RSA_MODULUS_LENGTH_IN_BYTES); byte[] akName = ProvisionUtils.generateAkName(akMod); // generate AES and HMAC keys from seed @@ -588,7 +604,7 @@ public static byte[] assembleCredential(final byte[] topSize, final byte[] integ * * @param akModulus modulus of an attestation key * @return the ak name byte array - * @throws java.security.NoSuchAlgorithmException Underlying SHA256 method used a bad algorithm + * @throws NoSuchAlgorithmException Underlying SHA256 method used a bad algorithm */ public static byte[] generateAkName(final byte[] akModulus) throws NoSuchAlgorithmException { byte[] namePrefix = HexUtils.hexStringToByteArray(AK_NAME_PREFIX); @@ -614,8 +630,8 @@ public static byte[] generateAkName(final byte[] akModulus) throws NoSuchAlgorit * @param context second portion of message used to generate key * @param sizeInBytes size of key to generate in bytes * @return the derived key - * @throws NoSuchAlgorithmException Wrong crypto algorithm selected - * @throws java.security.InvalidKeyException Invalid key used + * @throws NoSuchAlgorithmException Wrong crypto algorithm selected + * @throws InvalidKeyException Invalid key used */ public static byte[] cryptKDFa(final byte[] seed, final String label, final byte[] context, diff --git a/HIRS_AttestationCA/src/test/java/hirs/attestationca/persist/AttestationCertificateAuthorityTest.java b/HIRS_AttestationCA/src/test/java/hirs/attestationca/persist/AttestationCertificateAuthorityTest.java index c9721002c..8bebef547 100644 --- a/HIRS_AttestationCA/src/test/java/hirs/attestationca/persist/AttestationCertificateAuthorityTest.java +++ b/HIRS_AttestationCA/src/test/java/hirs/attestationca/persist/AttestationCertificateAuthorityTest.java @@ -73,7 +73,7 @@ import static org.mockito.Mockito.when; /** - * Test suite for {@link AttestationCertificateAuthorityService}. + * Test suite for {@link AttestationCertificateAuthorityServiceImpl}. */ @TestInstance(TestInstance.Lifecycle.PER_CLASS) // needed to use non-static BeforeAll public class AttestationCertificateAuthorityTest { @@ -123,7 +123,7 @@ public class AttestationCertificateAuthorityTest { + "e2 94"; private final Random random = new Random(); // object in test - private AttestationCertificateAuthorityService aca; + private AttestationCertificateAuthorityServiceImpl aca; private AccessAbstractProcessor abstractProcessor; // test key pair private KeyPair keyPair; @@ -174,7 +174,7 @@ public void setupTests() throws Exception { keyPair = keyPairGenerator.generateKeyPair(); //BeforeTest - aca = new AttestationCertificateAuthorityService(null, + aca = new AttestationCertificateAuthorityServiceImpl(null, null) { }; abstractProcessor = new AccessAbstractProcessor(keyPair.getPrivate(), 1); @@ -183,7 +183,7 @@ public void setupTests() throws Exception { } /** - * Tests {@link AttestationCertificateAuthorityService#processIdentityClaimTpm2(byte[])} + * Tests {@link AttestationCertificateAuthorityServiceImpl#processIdentityClaimTpm2(byte[])} * where the byte array is null. Expects an illegal argument exception to be thrown. */ @Test @@ -193,7 +193,7 @@ public void testProcessIdentityClaimTpm2NullRequest() { } /** - * Tests {@link AttestationCertificateAuthorityService#getLeafACACertPublicKey()}. + * Tests {@link AttestationCertificateAuthorityServiceImpl#getLeafACACertPublicKey()}. */ @Test public void testGetPublicKey() { @@ -412,7 +412,7 @@ public void testGenerateAttestation() throws Exception { } /** - * Tests {@link AttestationCertificateAuthorityService# + * Tests {@link AttestationCertificateAuthorityServiceImpl# * AttestationCertificateAuthority(SupplyChainValidationService, PrivateKey, * X509Certificate, StructConverter, CertificateManager, DeviceRegister, int, * DeviceManager, DBManager)}. diff --git a/package/linux/aca/aca_check_setup.sh b/package/linux/aca/aca_check_setup.sh index d6dd8fca1..b3a5b2e1b 100755 --- a/package/linux/aca/aca_check_setup.sh +++ b/package/linux/aca/aca_check_setup.sh @@ -167,15 +167,15 @@ check_pwds () { PRESENT=true echo "Checking if ACA passwords are present..." - if [ -z $hirs_pki_password ]; then + if [ -z "$hirs_pki_password" ]; then echo "ACA pki password not set" PRESENT=false fi - if [ -z $hirs_db_username ]; then + if [ -z "$hirs_db_username" ]; then echo "hirs db username not set" PRESENT=false fi - if [ -z $hirs_db_password ]; then + if [ -z "$hirs_db_password" ]; then echo "hirs db user password not set" PRESENT=false fi @@ -204,8 +204,8 @@ check_mysql_setup () { echo " Mysql client ($DB_CLIENT_CONF) is configured for command line use of TLS" fi - if [ ! -z $mysql_admin_password ]; then - mysql -u root --password=$mysql_admin_password -e "STATUS;" &> /dev/null + if [ ! -z "$mysql_admin_password" ]; then + mysql -u root --password="$mysql_admin_password" -e "STATUS;" &> /dev/null if [ $? -eq 0 ]; then echo "Mysql Root password verified" else @@ -213,10 +213,11 @@ check_mysql_setup () { fi if [ ! -z "${ARG_VERBOSE}" ]; then echo "Mysql status:" - mysql -u root --password=$mysql_admin_password -e "STATUS;" + mysql -u root --password="$mysql_admin_password" -e "STATUS;" echo "Listing mysql users:" - mysql -u root --password=$mysql_admin_password -e "Select user from mysql.user;" + mysql -u root --password="$mysql_admin_password" -e "Select user from mysql.user;" echo "Listing all databses:" + # shellcheck disable=SC2086 mysql -u root --password=$mysql_admin_password -e "show databases;" fi fi @@ -225,7 +226,7 @@ check_mysql_setup () { check_cert () { TRUST_STORE=$1 CERT=$2 - RESULT=$(openssl verify -CAfile "$TRUST_STORE" $CERT) + RESULT=$(openssl verify -CAfile "$TRUST_STORE" "$CERT") if [ $? -ne 0 ]; then ALL_CHECKS_PASSED=false ALL_CERTS_PASSED=false @@ -242,7 +243,7 @@ check_pki () { Skipping PKI Checks." fi - pushd $CERT_PATH$RSA_PATH > /dev/null + pushd $CERT_PATH$RSA_PATH > /dev/null || echo "Unable to push the directory to the stack" check_cert $RSA_TRUST_STORE $RSA_HIRS_ROOT check_cert $RSA_TRUST_STORE $RSA_HIRS_INTERMEDIATE check_cert $RSA_TRUST_STORE $RSA_HIRS_CA1 @@ -302,7 +303,7 @@ check_db () { fi - RESULT=$(mysqlshow --user=hirs_db --password=$hirs_db_password hirs_db| grep -o hirs_db) + RESULT=$(mysqlshow --user=hirs_db --password="$hirs_db_password" hirs_db| grep -o hirs_db) if [ "$RESULT" == "hirs_db" ]; then echo " The hirs_db database is visible by the hirs_db user" else @@ -311,26 +312,26 @@ check_db () { fi if [ ! -z "${ARG_VERBOSE}" ]; then echo " Show hirs_db user config using hirs_db password" - mysql -u hirs_db --password=$hirs_db_password -e "SHOW CREATE USER 'hirs_db'@'localhost';" \ + mysql -u hirs_db --password="$hirs_db_password" -e "SHOW CREATE USER 'hirs_db'@'localhost';" \ --ssl-ca=/etc/hirs/certificates/HIRS/rsa_3k_sha384_certs/HIRS_rsa_3k_sha384_Cert_Chain.pem \ --ssl-cert=/etc/hirs/certificates/HIRS/rsa_3k_sha384_certs/HIRS_db_client_rsa_3k_sha384.pem \ --ssl-key=/etc/hirs/certificates/HIRS/rsa_3k_sha384_certs/HIRS_db_client_rsa_3k_sha384.key echo "Mysql TLS configuration" - mysql -u root --password=$mysql_admin_password -e "SHOW VARIABLES LIKE '%ssl%'" + mysql -u root --password="$mysql_admin_password" -e "SHOW VARIABLES LIKE '%ssl%'" echo "TLS versions allowed on mariadb:" - mysql -u root --password=$mysql_admin_password -e "SHOW GLOBAL VARIABLES LIKE 'tls_version'"; + mysql -u root --password="$mysql_admin_password" -e "SHOW GLOBAL VARIABLES LIKE 'tls_version'"; echo "hirs_db user database access:" - mysql -u hirs_db --password=$hirs_db_password -e "SHOW DATABASES;"; + mysql -u hirs_db --password="$hirs_db_password" -e "SHOW DATABASES;"; echo "Privileges for the hirs_db user:" - mysql -u hirs_db --password=$hirs_db_password -e "SHOW GRANTS FOR 'hirs_db'@'localhost'" + mysql -u hirs_db --password="$hirs_db_password" -e "SHOW GRANTS FOR 'hirs_db'@'localhost'" echo "MYSQL Log:" - mysql -u root --password=$mysql_admin_password -e "SHOW GLOBAL VARIABLES LIKE 'log_error'" + mysql -u root --password="$mysql_admin_password" -e "SHOW GLOBAL VARIABLES LIKE 'log_error'" fi } # Check selinux status and files that require specific contexts check_selinux () { - if [ $ID = "ubuntu" ]; then + if [ "$ID" = "ubuntu" ]; then echo "Skipping selinux check on ubuntu" return fi diff --git a/package/linux/aca/aca_remove_setup.sh b/package/linux/aca/aca_remove_setup.sh index fa7db8be9..646be847a 100755 --- a/package/linux/aca/aca_remove_setup.sh +++ b/package/linux/aca/aca_remove_setup.sh @@ -8,7 +8,7 @@ SCRIPT_DIR=$( dirname -- "$( readlink -f -- "$0"; )"; ) OPTION_IN=$1; # per Fedora packing guidelines: $1 = 1 for an upgrade, 0 for a remove -if [ -z $1 ]; then OPTION_IN="2"; fi # Set if called by command line +if [ -z "$1" ]; then OPTION_IN="2"; fi # Set if called by command line case $OPTION_IN in "0") echo "Package removal requested" @@ -48,7 +48,7 @@ if [ ! -d "/opt/hirs" ]; then fi -source $SCRIPT_DIR/../db/mysql_util.sh +source "$SCRIPT_DIR"/../db/mysql_util.sh # Make sure mysql root password is available before continuing... check_mariadb_install @@ -57,9 +57,9 @@ check_mysql_root # remove the hrs-db and hirs_db user if [ $OPTION = "ACA_SET_REMOVE" ] || [ $OPTION = "ACA_PKG_REMOVE" ]; then - pushd $SCRIPT_DIR/../db/ &>/dev/null - ./db_drop.sh $DB_ADMIN_PWD - popd &>/dev/null + pushd "$SCRIPT_DIR"/../db/ &>/dev/null || echo "Unable to push directory to stack" + ./db_drop.sh "$DB_ADMIN_PWD" + popd &>/dev/null || echo "Unable to pop directory from stack" fi # remove pki files and config files if not installed by rpm diff --git a/package/linux/aca/aca_setup.sh b/package/linux/aca/aca_setup.sh index 399035847..91b75a49d 100755 --- a/package/linux/aca/aca_setup.sh +++ b/package/linux/aca/aca_setup.sh @@ -82,30 +82,33 @@ while [[ $# -gt 0 ]]; do ;; esac done + # Set default algorithms to rsa -if [ -z $ARG_ACA_ALG ]; then +if [ -z "$ARG_ACA_ALG" ]; then ACA_ALG="rsa" echo "Using default algorithm ($ACA_ALG) for Attestation Certs" fi -if [ -z $ARG_TLS_ALG ]; then + +if [ -z "$ARG_TLS_ALG" ]; then TLS_ALG="rsa" echo "Using default algorithm ($TLS_ALG) for the ACA portal" fi -if [ -z $ARG_DB_ALG ]; then + +if [ -z "$ARG_DB_ALG" ]; then DB_ALG="rsa" echo "Using default algorithm ($DB_ALG) for the Database" fi # Check for valid algorithms -if [ ! $ACA_ALG == "rsa" ] && [ ! $ACA_ALG == "ecc" ] ; then +if [ ! "$ACA_ALG" == "rsa" ] && [ ! "$ACA_ALG" == "ecc" ] ; then echo "Invalid ACA algorithm $ACA_ALG specified. Valid options are rsa or ecc." exit 1; fi -if [ ! $TLS_ALG == "rsa" ] && [ ! $TLS_ALG == "ecc" ] ; then +if [ ! "$TLS_ALG" == "rsa" ] && [ ! "$TLS_ALG" == "ecc" ] ; then echo "Invalid TLS algorithm $TLS_ALG specified. Valid options are rsa or ecc." exit 1; fi -if [ ! $DB_ALG == "rsa" ] && [ ! $DB_ALG == "ecc" ] ; then +if [ ! "$DB_ALG" == "rsa" ] && [ ! "$DB_ALG" == "ecc" ] ; then echo "Invalid DB algorithm $DB_ALG specified. Valid options are rsa or ecc." exit 1; fi @@ -127,7 +130,7 @@ fi #fi # Check for existing installation folders and exist if found -if [ -z $ARG_UNATTEND ]; then +if [ -z "$ARG_UNATTEND" ]; then if [ -d "/etc/hirs" ]; then echo "/etc/hirs exists, aborting install." exit 1 @@ -141,7 +144,7 @@ fi mkdir -p $HIRS_CONF_DIR $LOG_DIR $HIRS_JSON_DIR $ACA_OPT_DIR touch "$LOG_FILE" -pushd $SCRIPT_DIR &>/dev/null +pushd "$SCRIPT_DIR" &>/dev/null # Check if build environment is being used and set up property files if [ -f $PROP_FILE ]; then cp -n $PROP_FILE $HIRS_CONF_DIR/ @@ -163,12 +166,12 @@ if command -v git &> /dev/null; then git rev-parse --is-inside-work-tree &> /dev/null; if [ $? -eq 0 ]; then jarVersion=$(cat '../../../VERSION').$(date +%s).$(git rev-parse --short HEAD) - echo $jarVersion > $ACA_VERSION_FILE + echo "$jarVersion" > $ACA_VERSION_FILE fi fi # Set HIRS PKI password -if [ -z $HIRS_PKI_PWD ]; then +if [ -z "$HIRS_PKI_PWD" ]; then # Create a 32 character random password PKI_PASS=$(head -c 64 /dev/urandom | md5sum | tr -dc 'a-zA-Z0-9') echo "Using randomly generated password for the PKI key password" | tee -a "$LOG_FILE" @@ -178,7 +181,7 @@ if [ -z $HIRS_PKI_PWD ]; then fi if [ -z "${ARG_SKIP_PKI}" ]; then - ../pki/pki_setup.sh $LOG_FILE $PKI_PASS $ARG_UNATTEND + ../pki/pki_setup.sh "$LOG_FILE" "$PKI_PASS" "$ARG_UNATTEND" if [ $? -eq 0 ]; then echo "ACA PKI setup complete" | tee -a "$LOG_FILE" else @@ -190,7 +193,7 @@ if [ -z "${ARG_SKIP_PKI}" ]; then fi if [ -z "${ARG_SKIP_DB}" ]; then - ../db/db_create.sh $LOG_FILE $PKI_PASS $DB_ALG $ARG_UNATTEND + ../db/db_create.sh "$LOG_FILE" "$PKI_PASS" "$DB_ALG" "$ARG_UNATTEND" if [ $? -eq 0 ]; then echo "ACA database setup complete" | tee -a "$LOG_FILE" else @@ -220,16 +223,21 @@ fi sed -i '/aca.certificates.root-key-alias/d' $SPRING_PROP_FILE sed -i '/aca.current.public.key.algorithm/d' $SPRING_PROP_FILE if [ "$ACA_ALG" == "rsa" ]; then - # Add new lines for aca aliases - echo "aca.certificates.leaf-three-key-alias=HIRS_leaf_ca3_rsa_3k_sha384_key" >> $SPRING_PROP_FILE - echo "aca.certificates.intermediate-key-alias=HIRS_intermediate_ca_rsa_3k_sha384_key" >> $SPRING_PROP_FILE - echo "aca.certificates.root-key-alias=HIRS_root_ca_rsa_3k_sha384_key" >> $SPRING_PROP_FILE - echo "aca.current.public.key.algorithm=rsa" >> $SPRING_PROP_FILE + # Add new lines for aca aliases for the RSA public key algorithm + { + echo "aca.certificates.leaf-three-key-alias=HIRS_leaf_ca3_rsa_3k_sha384_key" + echo "aca.certificates.intermediate-key-alias=HIRS_intermediate_ca_rsa_3k_sha384_key" + echo "aca.certificates.root-key-alias=HIRS_root_ca_rsa_3k_sha384_key" + echo "aca.current.public.key.algorithm=rsa" + } >> $SPRING_PROP_FILE elif [ "$ACA_ALG" == "ecc" ]; then - echo "aca.certificates.leaf-three-key-alias=HIRS_leaf_ca3_ecc_512_sha384_key" >> $SPRING_PROP_FILE - echo "aca.certificates.intermediate-key-alias=HIRS_intermediate_ca_ecc_512_sha384_key" >> $SPRING_PROP_FILE - echo "aca.certificates.root-key-alias=HIRS_root_ca_ecc_512_sha384_key" >> $SPRING_PROP_FILE - echo "aca.current.public.key.algorithm=ecc" >> $SPRING_PROP_FILE + { + # Add new lines for aca aliases for the ECC public key algorithm + echo "aca.certificates.leaf-three-key-alias=HIRS_leaf_ca3_ecc_512_sha384_key" + echo "aca.certificates.intermediate-key-alias=HIRS_intermediate_ca_ecc_512_sha384_key" + echo "aca.certificates.root-key-alias=HIRS_root_ca_ecc_512_sha384_key" + echo "aca.current.public.key.algorithm=ecc" + } >> $SPRING_PROP_FILE fi echo "ACA setup complete" | tee -a "$LOG_FILE" From 9d14600ba63482cd57a2e6558d19b95d1236c1b1 Mon Sep 17 00:00:00 2001 From: ThatSilentCoder <184309164+ThatSilentCoder@users.noreply.github.com> Date: Mon, 23 Feb 2026 15:54:08 -0500 Subject: [PATCH 04/25] v3.1_issue_1090: Fixing warnings/errors pointed out by shell plugin in bash scripts. Took care of a majority of them. --- package/linux/aca/aca_bootRun.sh | 6 +-- package/linux/aca/aca_check_setup.sh | 47 ++++++++++---------- package/linux/aca/aca_remove_setup.sh | 3 +- package/linux/aca/check_for_aca.sh | 2 +- package/linux/db/db_create.sh | 64 ++++++++++++++------------- package/linux/db/db_drop.sh | 14 +++--- package/linux/db/mysql_util.sh | 32 +++++++------- 7 files changed, 85 insertions(+), 83 deletions(-) mode change 100644 => 100755 package/linux/aca/check_for_aca.sh diff --git a/package/linux/aca/aca_bootRun.sh b/package/linux/aca/aca_bootRun.sh index 20536795f..b469b0894 100755 --- a/package/linux/aca/aca_bootRun.sh +++ b/package/linux/aca/aca_bootRun.sh @@ -79,7 +79,7 @@ fi set -- "${POSITIONAL_ARGS[@]}" # restore positional parameters -source $SCRIPT_DIR/../db/mysql_util.sh +source "$SCRIPT_DIR"/../db/mysql_util.sh if [ $ALG = "RSA" ]; then CERT_PATH="/etc/hirs/certificates/HIRS/$RSA_PATH" @@ -143,9 +143,9 @@ else echo "Booting the ACA from a war file..." if [ "$DEBUG_ACA" == YES ]; then echo "... in debug" - java $DEBUG_OPTIONS -jar $WAR_PATH --spring.config.location=$SPRING_PROP_FILE & + java "$DEBUG_OPTIONS" -jar "$WAR_PATH" --spring.config.location=$SPRING_PROP_FILE & else - java -jar $WAR_PATH --spring.config.location=$SPRING_PROP_FILE & + java -jar "$WAR_PATH" --spring.config.location=$SPRING_PROP_FILE & fi exit 0 fi diff --git a/package/linux/aca/aca_check_setup.sh b/package/linux/aca/aca_check_setup.sh index b3a5b2e1b..1ffbceee7 100755 --- a/package/linux/aca/aca_check_setup.sh +++ b/package/linux/aca/aca_check_setup.sh @@ -40,11 +40,11 @@ DB_CLIENT_CONF="/etc/my.cnf.d/client.cnf" ALL_CHECKS_PASSED=true ALL_CERTS_PASSED=true -source $SCRIPT_DIR/../db/mysql_util.sh +source "$SCRIPT_DIR"/../db/mysql_util.sh source /etc/os-release # Setup distro specifc paths and variables -if [ $ID = "ubuntu" ]; then +if [ "$ID" = "ubuntu" ]; then DB_SRV_CONF="/etc/mysql/mariadb.conf.d/50-server.cnf" DB_CLIENT_CONF="/etc/mysql/mariadb.conf.d/50-client.cnf" fi @@ -85,13 +85,13 @@ fi echo "Checking HIRS ACA Setup on this device..." # Check if aca setup was performed # Check is RPM was installed via RPM package - if [ $ID = "rhel" ]; then + if [ "$ID" = "rhel" ]; then echo "RHEL distro detected" rpm -q --quiet HIRS_AttestationCA - elif [ $ID = 'ubuntu' ]; then + elif [ "$ID" = 'ubuntu' ]; then echo "Ubuntu distro detected" dpkg -l "hirs-attestationca" > /dev/null - elif [ $ID = 'rocky' ]; then + elif [ "$ID" = 'rocky' ]; then echo "Rocky distro detected" rpm -q --quiet HIRS_AttestationCA else @@ -99,7 +99,7 @@ echo "Checking HIRS ACA Setup on this device..." fi if [ $? -eq 0 ]; then echo "HIRS ACA was installed via an OS package on this device." - if [ $SYSD_SERVICE = true ]; then + if [ "$SYSD_SERVICE" = true ]; then systemctl is-active --quiet hirs-aca if [[ $? -eq 0 ]]; then echo " The hirs-aca service is active" @@ -191,34 +191,33 @@ check_mysql_setup () { # make sure mysql is running and restart if its not... check_mysql # Check DB server/client TLS setup. - if [[ $(cat "$DB_SRV_CONF" | grep -c "HIRS") < 1 ]]; then + if [[ "$(grep -c "HIRS" "$DB_SRV_CONF")" -lt 1 ]]; then echo " Mysql server ($DB_SRV_CONF) is NOT configured for Server Side TLS" ALL_CHECKS_PASSED=false else echo " Mysql server ($DB_SRV_CONF) is configured for Server Side TLS" fi - if [[ $(cat "$DB_CLIENT_CONF" | grep -c "HIRS") < 1 ]]; then + if [[ "$(grep -c "HIRS" "$DB_CLIENT_CONF")" -lt 1 ]]; then echo " Mysql client ($DB_CLIENT_CONF)is NOT configured for command line use of TLS without provding key/cert ino the commandline" ALL_CHECKS_PASSED=false else echo " Mysql client ($DB_CLIENT_CONF) is configured for command line use of TLS" fi - if [ ! -z "$mysql_admin_password" ]; then + if [ -n "$mysql_admin_password" ]; then mysql -u root --password="$mysql_admin_password" -e "STATUS;" &> /dev/null if [ $? -eq 0 ]; then echo "Mysql Root password verified" else echo "Mysql Root password verification failed!" fi - if [ ! -z "${ARG_VERBOSE}" ]; then + if [ -n "${ARG_VERBOSE}" ]; then echo "Mysql status:" mysql -u root --password="$mysql_admin_password" -e "STATUS;" echo "Listing mysql users:" mysql -u root --password="$mysql_admin_password" -e "Select user from mysql.user;" - echo "Listing all databses:" - # shellcheck disable=SC2086 - mysql -u root --password=$mysql_admin_password -e "show databases;" + echo "Listing all databases:" + mysql -u root --password="$mysql_admin_password" -e "show databases;" fi fi } @@ -231,8 +230,8 @@ check_cert () { ALL_CHECKS_PASSED=false ALL_CERTS_PASSED=false fi - if [ ! -z "${ARG_VERBOSE}" ]; then - echo " "$RESULT + if [ -n "${ARG_VERBOSE}" ]; then + echo " ""$RESULT" fi } @@ -254,8 +253,8 @@ check_pki () { check_cert $RSA_TRUST_STORE $RSA_DB_CLIENT_CERT check_cert $RSA_TRUST_STORE $RSA_WEB_TLS_CERT - popd > /dev/null - pushd $CERT_PATH$ECC_PATH > /dev/null + popd > /dev/null || echo "Unable to pop the directory from the stack" + pushd $CERT_PATH$ECC_PATH > /dev/null || echo "Unable to push the directory to the stack" check_cert $ECC_TRUST_STORE $ECC_HIRS_ROOT check_cert $ECC_TRUST_STORE $ECC_HIRS_INTERMEDIATE check_cert $ECC_TRUST_STORE $ECC_HIRS_CA1 @@ -265,7 +264,7 @@ check_pki () { check_cert $ECC_TRUST_STORE $ECC_DN_SRV_CERT check_cert $ECC_TRUST_STORE $ECC_DB_CLIENT_CERT check_cert $ECC_TRUST_STORE $ECC_WEB_TLS_CERT - popd > /dev/null + popd > /dev/null || echo "Unable to pop the directory from the stack" if [ -z "${ARG_VERBOSE}" ]; then if [ $ALL_CERTS_PASSED == true ]; then @@ -273,15 +272,15 @@ check_pki () { else echo " Error: There were error in the certificates under $CERT_PATH" fi - keytool -list -keystore /etc/hirs/certificates/HIRS/TrustStore.jks -storepass $hirs_pki_password | grep hirs | sed -e 's/^/ /' > /dev/null + keytool -list -keystore /etc/hirs/certificates/HIRS/TrustStore.jks -storepass "$hirs_pki_password" | grep hirs | sed -e 's/^/ /' > /dev/null else #verbose echo " Checking TrustStore, aliases, and pki password" echo " Truststore alias list:" - keytool -list -keystore /etc/hirs/certificates/HIRS/TrustStore.jks -storepass $hirs_pki_password | sed -e 's/^/ /' 2>/dev/null + keytool -list -keystore /etc/hirs/certificates/HIRS/TrustStore.jks -storepass "$hirs_pki_password" | sed -e 's/^/ /' 2>/dev/null echo " Checking KeyStore, aliases, and pki password" echo " Keystore alias list:" - keytool -list -keystore /etc/hirs/certificates/HIRS/KeyStore.jks -storepass $hirs_pki_password | sed -e 's/^/ /' 2>/dev/null + keytool -list -keystore /etc/hirs/certificates/HIRS/KeyStore.jks -storepass "$hirs_pki_password" | sed -e 's/^/ /' 2>/dev/null fi if [ $? -eq 0 ]; then @@ -294,7 +293,7 @@ check_pki () { check_db () { echo "Checking DB server TLS configuration..." - RESULT=$(mysql -u root --password=$mysql_admin_password -e "SHOW VARIABLES LIKE '%have_ssl%'" | grep -o YES ) + RESULT=$(mysql -u root --password="$mysql_admin_password" -e "SHOW VARIABLES LIKE '%have_ssl%'" | grep -o YES ) if [ "$RESULT" == "YES" ]; then echo " Mysql Server side TLS is enabled:" else @@ -310,7 +309,7 @@ check_db () { echo " Error: The hirs_db database is NOT visible by the hirs_db user" ALL_CHECKS_PASSED=false fi - if [ ! -z "${ARG_VERBOSE}" ]; then + if [ -n "${ARG_VERBOSE}" ]; then echo " Show hirs_db user config using hirs_db password" mysql -u hirs_db --password="$hirs_db_password" -e "SHOW CREATE USER 'hirs_db'@'localhost';" \ --ssl-ca=/etc/hirs/certificates/HIRS/rsa_3k_sha384_certs/HIRS_rsa_3k_sha384_Cert_Chain.pem \ @@ -361,7 +360,7 @@ check_selinux () { check_fips () { echo "Checking FIPS mode on this device..." fips=$(sysctl -a 2>&1 | grep crypto.fips_enabled) - echo " "$fips + echo " ""$fips" } # Run Checks diff --git a/package/linux/aca/aca_remove_setup.sh b/package/linux/aca/aca_remove_setup.sh index 646be847a..6e20a0d01 100755 --- a/package/linux/aca/aca_remove_setup.sh +++ b/package/linux/aca/aca_remove_setup.sh @@ -25,7 +25,6 @@ case $OPTION_IN in *) echo "$1 is an unknown parameter for aca_remove_setup" exit 1 - break ;; esac @@ -81,5 +80,5 @@ fi # Remove current ACA process echo "Shutting down the aca..." -ps axf | grep HIRS_AttestationCAPortal.war | grep -v grep | awk '{print "kill " $1}' | sh >/dev/null 2>&1 +pgrep HIRS_AttestationCAPortal.war | xargs kill >/dev/null 2>&1 echo "ACA setup removal complete." diff --git a/package/linux/aca/check_for_aca.sh b/package/linux/aca/check_for_aca.sh old mode 100644 new mode 100755 index 62c573780..21dd8fa77 --- a/package/linux/aca/check_for_aca.sh +++ b/package/linux/aca/check_for_aca.sh @@ -8,7 +8,7 @@ ACA_URL="https://localhost:8443/HIRS_AttestationCAPortal/portal/index" echo "Waiting for tomcat..." count=0 - until [ "`curl --silent --connect-timeout 1 --insecure -I $ACA_URL | grep -c 'Date'`" == 1 ] || [[ $count -gt 20 ]]; do + until [ "$(curl --silent --connect-timeout 1 --insecure -I $ACA_URL | grep -c 'Date')" == 1 ] || [[ $count -gt 20 ]]; do ((count++)) sleep 1 done diff --git a/package/linux/db/db_create.sh b/package/linux/db/db_create.sh index f88a258cb..fd27d786e 100755 --- a/package/linux/db/db_create.sh +++ b/package/linux/db/db_create.sh @@ -19,7 +19,7 @@ SCRIPT_DIR=$( dirname -- "$( readlink -f -- "$0"; )"; ) SPRING_PROP_FILE="/etc/hirs/aca/application.properties" ACA_PROP_FILE="/etc/hirs/aca/aca.properties" DB_ADMIN_PWD="" -# Db Configuration fileis, use RHELpaths as default +# Db Configuration files, use RHELpaths as default DB_SRV_CONF="/etc/my.cnf.d/mariadb-server.cnf" DB_CLIENT_CONF="/etc/my.cnf.d/client.cnf" # Default Certificates @@ -47,22 +47,22 @@ mkdir -p /etc/hirs/aca/ mkdir -p /var/log/hirs/ source $ACA_PROP_FILE -source $SCRIPT_DIR/mysql_util.sh +source "$SCRIPT_DIR"/mysql_util.sh source /etc/os-release # Setup distro specifc paths and variables -if [ $ID = "ubuntu" ]; then +if [ "$ID" = "ubuntu" ]; then DB_SRV_CONF="/etc/mysql/mariadb.conf.d/50-server.cnf" DB_CLIENT_CONF="/etc/mysql/mariadb.conf.d/50-client.cnf" mkdir -p /var/log/mariadb >> /dev/null - if [[ $(cat "$DB_SRV_CONF" | grep -c "log-error") < 1 ]]; then + if [[ "$(grep -c "log-error" "$DB_SRV_CONF")" -lt 1 ]]; then echo "log_error=/var/log/mariadb/mariadb.log" >> $DB_SRV_CONF echo "tls_version = TLSv1.2,TLSv1.3" >> $DB_SRV_CONF fi fi touch $ACA_PROP_FILE -touch $LOG_FILE +touch "$LOG_FILE" touch $DB_SRV_CONF touch $DB_LOG_FILE @@ -73,19 +73,19 @@ check_mysql_root_pwd () { # Check if property file exists and look for properties if [ -f $ACA_PROP_FILE ]; then source $ACA_PROP_FILE - if [ ! -z $hirs_pki_password ]; then PKI_PASS=$hirs_pki_password; fi - if [ ! -z $mysql_admin_password ]; then HIRS_MYSQL_ROOT_PWD=$mysql_admin_password; fi - if [ ! -z $hirs_db_password ]; then HIRS_DB_PWD=$hirs_db_password; fi + if [ -n "$hirs_pki_password" ]; then PKI_PASS=$hirs_pki_password; fi + if [ -n "$mysql_admin_password" ]; then HIRS_MYSQL_ROOT_PWD=$mysql_admin_password; fi + if [ -n "$hirs_db_password" ]; then HIRS_DB_PWD=$hirs_db_password; fi fi fi - if [ -z $HIRS_MYSQL_ROOT_PWD ]; then + if [ -z "$HIRS_MYSQL_ROOT_PWD" ]; then # Create a 32 character random password echo "Using randomly generated password for the DB admin" | tee -a "$LOG_FILE" DB_ADMIN_PWD=$(head -c 64 /dev/urandom | md5sum | tr -dc 'a-zA-Z0-9') echo "DB Admin will be set to $DB_ADMIN_PWD , please make note for next mysql use." # Check UNATTENDED flag set m if not then prompt user for permission ot store mysql root password - if [ -z $UNATTENDED ]; then + if [ -z "$UNATTENDED" ]; then read -p "Do you wish to save this password to the aca.properties file? " confirm if [[ $confirm == [yY] || $confirm == [yY][eE][sS] ]]; then echo "mysql_admin_password=$DB_ADMIN_PWD" >> $ACA_PROP_FILE @@ -117,18 +117,20 @@ check_mysql_root_pwd () { set_mysql_server_tls () { # Check DB server setup. If HIRS ssl params dont exist then we need to add them. - if [[ $(cat "$DB_SRV_CONF" | grep -c "HIRS") < 1 ]]; then + if [[ "$(grep -c "HIRS" "$DB_SRV_CONF")" -lt 1 ]]; then # Add TLS files to my.cnf echo "Updating $DB_SRV_CONF with ssl parameters..." | tee -a "$LOG_FILE" - echo "ssl_ca=$SSL_DB_SRV_CHAIN" >> "$DB_SRV_CONF" - echo "ssl_cert=$SSL_DB_SRV_CERT" >> "$DB_SRV_CONF" - echo "ssl_key=$SSL_DB_SRV_KEY" >> "$DB_SRV_CONF" - # The following arent avialble in Mariadb 10.3 + { + echo "ssl_ca=$SSL_DB_SRV_CHAIN" + echo "ssl_cert=$SSL_DB_SRV_CERT" + echo "ssl_key=$SSL_DB_SRV_KEY" + } >> "$DB_SRV_CONF" + # The following arent available in Mariadb 10.3 #echo "tls_version=TLSv1.2,TLSv1.3" >> "$DB_SRV_CONF" #echo "require_secure_transport=ON" >> "$DB_SRV_CONF" # Make sure mysql can access them - chown mysql:mysql $SSL_DB_SRV_CHAIN $SSL_DB_SRV_CERT $SSL_DB_SRV_KEY + chown mysql:mysql "$SSL_DB_SRV_CHAIN" "$SSL_DB_SRV_CERT" "$SSL_DB_SRV_KEY" chmod 644 $DB_SRV_CONF $DB_CLIENT_CONF # Make selinux contexts for config files, if selinux is enabled if [[ $ID = "rhel" ]] || [[ $ID = "rocky" ]] ||[[ $ID = "fedora" ]]; then @@ -142,18 +144,20 @@ set_mysql_server_tls () { fi fi else - echo "mysql.cnf contians existing entry for ssl, skipping..." | tee -a "$LOG_FILE" + echo "mysql.cnf contains existing entry for ssl, skipping..." | tee -a "$LOG_FILE" fi } set_mysql_client_tls () { # Update ACA property file with client cert info, if not there already -if [[ $(cat "$DB_CLIENT_CONF" | grep -c "HIRS") < 1 ]]; then +if [[ "$(grep -c "HIRS" "$DB_CLIENT_CONF")" -lt 1 ]]; then echo "Updating $DB_CLIENT_CONF with ssl parameters..." | tee -a "$LOG_FILE" - echo "ssl_ca=$SSL_DB_CLIENT_CHAIN" >> $DB_CLIENT_CONF - echo "ssl_cert=$SSL_DB_CLIENT_CERT" >> $DB_CLIENT_CONF - echo "ssl_key=$SSL_DB_CLIENT_KEY" >> $DB_CLIENT_CONF - chown mysql:mysql $SSL_DB_CLIENT_CHAIN $SSL_DB_CLIENT_CERT $SSL_DB_CLIENT_KEY + { + echo "ssl_ca=$SSL_DB_CLIENT_CHAIN" + echo "ssl_cert=$SSL_DB_CLIENT_CERT" + echo "ssl_key=$SSL_DB_CLIENT_KEY" + } >> $DB_CLIENT_CONF + chown mysql:mysql "$SSL_DB_CLIENT_CHAIN" "$SSL_DB_CLIENT_CERT" "$SSL_DB_CLIENT_KEY" # Make selinux contexts for config files, if selinux is enabled if [[ $ID = "rhel" ]] || [[ $ID = "rocky" ]] ||[[ $ID = "fedora" ]]; then command -v selinuxenabled > /dev/null @@ -173,7 +177,7 @@ set_hirs_db_pwd () { check_hirs_db if [[ $HIRS_DB_USER_EXISTS != "1" ]]; then # Check if Mysql HIRS DB password set by system variable or set to random number - if [ -z $HIRS_DB_PWD ]; then + if [ -z "$HIRS_DB_PWD" ]; then HIRS_DB_PWD=$(head -c 64 /dev/urandom | md5sum | tr -dc 'a-zA-Z0-9') fi # Add key/values only if they dont exist @@ -202,13 +206,13 @@ create_hirs_db_with_tls () { echo "hirs_db already exists, skipping" else # Check if hirs_db not created and create it if it wasn't - mysqlshow --user=root --password="$DB_ADMIN_PWD" | grep "hirs_db" >> $LOG_FILE 2>&1 + mysqlshow --user=root --password="$DB_ADMIN_PWD" | grep "hirs_db" >> "$LOG_FILE" 2>&1 if [ $? -eq 0 ]; then echo "hirs_db exists, skipping hirs_db create" else - mysql -u root --password=$DB_ADMIN_PWD < $MYSQL_DIR/db_create.sql - mysql -u root --password=$DB_ADMIN_PWD < $MYSQL_DIR/secure_mysql.sql - mysql -u root --password=$DB_ADMIN_PWD -e "SET PASSWORD FOR 'hirs_db'@'localhost' = PASSWORD('"$HIRS_DB_PWD"'); FLUSH PRIVILEGES;"; + mysql -u root --password="$DB_ADMIN_PWD" < "$MYSQL_DIR"/db_create.sql + mysql -u root --password="$DB_ADMIN_PWD" < "$MYSQL_DIR"/secure_mysql.sql + mysql -u root --password="$DB_ADMIN_PWD" -e "SET PASSWORD FOR 'hirs_db'@'localhost' = PASSWORD('"$HIRS_DB_PWD"'); FLUSH PRIVILEGES;"; echo "**** Setting hirs_db pwd to $HIRS_DB_PWD ***" fi fi @@ -219,7 +223,7 @@ create_hibernate_url () { ALG=$1 db_username=$2 - if [ $ALG = "rsa" ]; then + if [ "$ALG" = "rsa" ]; then CERT_PATH="/etc/hirs/certificates/HIRS/$RSA_PATH" CERT_CHAIN="$CERT_PATH/HIRS_rsa_3k_sha384_Cert_Chain.pem" CLIENT_DB_P12=$CERT_PATH/HIRS_db_client_rsa_3k_sha384.p12 @@ -241,7 +245,7 @@ keyStorePassword=$PKI_PASS&\ keyStore="$CLIENT_DB_P12" " if [[ $(grep -c "hibernate.connection.url" $SPRING_PROP_FILE) -eq 0 ]]; then - echo $CONNECTOR_URL >> $SPRING_PROP_FILE + echo "$CONNECTOR_URL" >> $SPRING_PROP_FILE fi } @@ -255,5 +259,5 @@ set_hirs_db_pwd create_hirs_db_with_tls set_mysql_server_tls set_mysql_client_tls -create_hibernate_url $DB_ALG "hirs_db" +create_hibernate_url "$DB_ALG" "hirs_db" mysqld_reboot diff --git a/package/linux/db/db_drop.sh b/package/linux/db/db_drop.sh index f409f90e7..32607330d 100755 --- a/package/linux/db/db_drop.sh +++ b/package/linux/db/db_drop.sh @@ -7,7 +7,7 @@ LOG_FILE=/dev/null DB_ADMIN_PWD=$1 #source /etc/hirs/aca/aca.properties; -source $SCRIPT_DIR/mysql_util.sh +source "$SCRIPT_DIR"/mysql_util.sh source /etc/os-release check_systemd @@ -19,7 +19,7 @@ if [ "$EUID" -ne 0 ] fi # Setup distro specifc paths and variables -if [ $ID = "ubuntu" ]; then +if [ "$ID" = "ubuntu" ]; then DB_SRV_CONF="/etc/mysql/mariadb.conf.d/50-server.cnf" DB_CLIENT_CONF="/etc/mysql/mariadb.conf.d/50-client.cnf" fi @@ -32,16 +32,16 @@ fi echo "dropping hirs_db database" - mysql -u root --password=$DB_ADMIN_PWD -e "FLUSH HOSTS; FLUSH LOGS; FLUSH STATUS; FLUSH PRIVILEGES; FLUSH USER_RESOURCES" - mysql -u root --password=$DB_ADMIN_PWD -e "DROP USER 'hirs_db'@'localhost';" - mysql -u root --password=$DB_ADMIN_PWD -e "DROP DATABASE IF EXISTS hirs_db;" + mysql -u root --password="$DB_ADMIN_PWD" -e "FLUSH HOSTS; FLUSH LOGS; FLUSH STATUS; FLUSH PRIVILEGES; FLUSH USER_RESOURCES" + mysql -u root --password="$DB_ADMIN_PWD" -e "DROP USER 'hirs_db'@'localhost';" + mysql -u root --password="$DB_ADMIN_PWD" -e "DROP DATABASE IF EXISTS hirs_db;" echo "hirs_db database and hirs_db user removed" # reset the mysql root if the password was left in the properties fiel -if [ ! -z $DB_ADMIN_PWD ]; then +if [ -n "$DB_ADMIN_PWD" ]; then echo "Resetting mysql root password to empty" - mysql -u root --password=$DB_ADMIN_PWD -e "SET PASSWORD FOR "root@localhost" = PASSWORD('');" + mysql -u root --password="$DB_ADMIN_PWD" -e "SET PASSWORD FOR "root@localhost" = PASSWORD('');" mysql -u "root" -e "FLUSH LOGS;" else echo "Note root password was NOT reset" diff --git a/package/linux/db/mysql_util.sh b/package/linux/db/mysql_util.sh index e16f0acc6..da62a7c67 100755 --- a/package/linux/db/mysql_util.sh +++ b/package/linux/db/mysql_util.sh @@ -33,7 +33,7 @@ check_systemd () { # Check for mysql command line check_mariadb_install () { type mysql >/dev/null 2>&1 && installed=true || installed=false - if [ $installed = true ]; then + if [ "$installed" = true ]; then echo "mysql has been installed" else echo "mysql has NOT been installed, aborting install" @@ -45,12 +45,12 @@ start_mysqlsd () { PRINT_STATUS=$1 PROCESS="mysqld" source /etc/os-release - if [ $ID = "ubuntu" ]; then + if [ "$ID" = "ubuntu" ]; then PROCESS="mariadb" fi # Make sure log file has correct permissions chown -R mysql:mysql /var/lib/mysql/ >> "$LOG_FILE" - if [ $DB_SERVICE = true ]; then + if [ "$DB_SERVICE" = true ]; then systemctl is-active --quiet mariadb if [[ $? -ne 0 ]]; then echo "mariadb service not running , attempting to restart" @@ -78,12 +78,12 @@ start_mysqlsd () { check_mysql () { PROCESS="mysqld" source /etc/os-release - if [ $ID = "ubuntu" ]; then + if [ "$ID" = "ubuntu" ]; then PROCESS="mariadb" fi echo "Checking mysqld status..." - if [ $DB_SERVICE = true ]; then + if [ "$DB_SERVICE" = true ]; then systemctl is-active --quiet mariadb if [[ $? -ne 0 ]]; then echo "mariadb service not running , attempting to restart" @@ -97,7 +97,7 @@ check_mysql () { fi fi - if [ $DB_SERVICE = true ]; then + if [ "$DB_SERVICE" = true ]; then systemctl is-active --quiet mariadb if [[ $? -eq 0 ]]; then echo "mariadb service started" | tee -a "$LOG_FILE"; @@ -124,7 +124,7 @@ fi # Check for mysql root password , abort if not available check_mysql_root () { - if [ -z $HIRS_MYSQL_ROOT_PWD ]; then + if [ -z "$HIRS_MYSQL_ROOT_PWD" ]; then if [ ! -f /etc/hirs/aca/aca.properties ]; then echo "aca.properties does not exist." | tee -a "$LOG_FILE"; else @@ -136,7 +136,7 @@ check_mysql_root () { fi # Allow user to enter password if not using env variabel or file -if [ -z $DB_ADMIN_PWD ]; then +if [ -z "$DB_ADMIN_PWD" ]; then read -p "Enter mysql root password" DB_ADMIN_PWD else DB_ADMIN_PWD=$mysql_admin_password @@ -156,7 +156,7 @@ $(mysql -u root -p$DB_ADMIN_PWD -e 'quit' &> /dev/null); check_hirs_db_user () { PRINT_STATUS=$1 -HIRS_DB_USER_EXISTS="$(mysql -uroot --password=$DB_ADMIN_PWD -sse "SELECT EXISTS(SELECT 1 FROM mysql.user WHERE user = 'hirs_db')")" +HIRS_DB_USER_EXISTS="$(mysql -uroot --password="$DB_ADMIN_PWD" -sse "SELECT EXISTS(SELECT 1 FROM mysql.user WHERE user = 'hirs_db')")" if [[ $HIRS_DB_USER_EXISTS == "1" ]]; then if [[ $PRINT_STATUS == "-p" ]];then echo " hirs_db user exists" | tee -a "$LOG_FILE"; fi; else @@ -166,7 +166,7 @@ HIRS_DB_USER_EXISTS="$(mysql -uroot --password=$DB_ADMIN_PWD -sse "SELECT EXISTS check_hirs_db () { PRINT_STATUS=$1 -HIRS_DB_EXISTS="$(mysql -uroot --password=$DB_ADMIN_PWD -e "SHOW DATABASES" | grep hirs_db)" +HIRS_DB_EXISTS="$(mysql -uroot --password="$DB_ADMIN_PWD" -e "SHOW DATABASES" | grep hirs_db)" if [[ $HIRS_DB_EXISTS == "hirs_db" ]]; then if [[ $PRINT_STATUS == "-p" ]];then echo " hirs_db database exists" | tee -a "$LOG_FILE"; fi; else @@ -187,7 +187,7 @@ check_db_cleared () { else echo " hirs_db user does not exist" | tee -a "$LOG_FILE"; fi - HIRS_DB_EXISTS=`mysql -uroot -e "SHOW DATABASES" | grep hirs_db` + HIRS_DB_EXISTS=$(mysql -uroot -e "SHOW DATABASES" | grep hirs_db) if [[ $HIRS_DB_EXISTS == "1" ]]; then echo " hirs_db databse exists" | tee -a "$LOG_FILE"; else @@ -200,7 +200,7 @@ $(mysql -u root -e 'quit' &> /dev/null); if [ $? -eq 0 ]; then HIRS_DB_USER_EXISTS="$(mysql -uroot -sse "SELECT EXISTS(SELECT 1 FROM mysql.user WHERE user = 'hirs_db')")" if [[ $HIRS_DB_USER_EXISTS == "1" ]]; then - mysql -u root --password=$DB_ADMIN_PWD -e "DROP USER 'hirs_db'@'localhost';" + mysql -u root --password="$DB_ADMIN_PWD" -e "DROP USER 'hirs_db'@'localhost';" echo "hirs_db user found and deleted" fi fi @@ -209,7 +209,7 @@ $(mysql -u root -e 'quit' &> /dev/null); clear_hirs_db () { $(mysql -u root -e 'quit' &> /dev/null); if [ $? -eq 0 ]; then - mysql -u root --password=$DB_ADMIN_PWD -e "DROP DATABASE IF EXISTS hirs_db;" + mysql -u root --password="$DB_ADMIN_PWD" -e "DROP DATABASE IF EXISTS hirs_db;" fi } @@ -232,18 +232,18 @@ mysqld_reboot () { # reboot mysql server PROCESS="mysqld" source /etc/os-release - if [ $ID = "ubuntu" ]; then + if [ "$ID" = "ubuntu" ]; then PROCESS="mariadb" fi echo "Restarting Mariadb ...." | tee -a "$LOG_FILE"; - if [ $DB_SERVICE = true ]; then + if [ "$DB_SERVICE" = true ]; then echo "Shutting down and restarting mysql service" | tee -a "$LOG_FILE"; systemctl stop mariadb >> "$LOG_FILE"; sleep 2 systemctl start mariadb >> "$LOG_FILE"; else # No systemd echo "Shutting down and restarting mysql process" | tee -a "$LOG_FILE"; - mysql -u root --password=$DB_ADMIN_PWD -e "SHUTDOWN" + mysql -u root --password="$DB_ADMIN_PWD" -e "SHUTDOWN" sleep 1 # Make sure mysql has stopped if [[ $(pgrep -c $PROCESS) -ne 0 ]]; then From 4f1e778ee39c05053a624f76b984c12183bd832e Mon Sep 17 00:00:00 2001 From: ThatSilentCoder <184309164+ThatSilentCoder@users.noreply.github.com> Date: Tue, 24 Feb 2026 17:58:33 -0500 Subject: [PATCH 05/25] v3.1_issue_1090: Fixed a lot of tests. Removed a lot of unused methods in the utils class. --- .../config/spotbugs/spotbugs-exclude.xml | 4 - ...ionCertificateAuthorityRestController.java | 14 +- ...ttestationCertificateAuthorityService.java | 9 +- ...tationCertificateAuthorityServiceImpl.java | 8 +- .../CertificateProcessingException.java | 4 +- .../IdentityProcessingException.java | 4 +- .../exceptions/UnexpectedServerException.java | 4 +- .../CertificateRequestProcessor.java | 7 +- .../provision/IdentityClaimProcessor.java | 6 +- .../provision/helper/ProvisionUtils.java | 201 +---- ...tationCertificateAuthorityServiceTest.java | 468 +++++++++++ .../AttestationCertificateAuthorityTest.java | 795 ------------------ 12 files changed, 501 insertions(+), 1023 deletions(-) create mode 100644 HIRS_AttestationCA/src/test/java/hirs/attestationca/persist/AttestationCertificateAuthorityServiceTest.java delete mode 100644 HIRS_AttestationCA/src/test/java/hirs/attestationca/persist/AttestationCertificateAuthorityTest.java diff --git a/HIRS_AttestationCA/config/spotbugs/spotbugs-exclude.xml b/HIRS_AttestationCA/config/spotbugs/spotbugs-exclude.xml index acdcaa9b1..8641638f1 100644 --- a/HIRS_AttestationCA/config/spotbugs/spotbugs-exclude.xml +++ b/HIRS_AttestationCA/config/spotbugs/spotbugs-exclude.xml @@ -15,9 +15,5 @@ - - - - diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/AttestationCertificateAuthorityRestController.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/AttestationCertificateAuthorityRestController.java index ff991839b..e3ecf4801 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/AttestationCertificateAuthorityRestController.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/AttestationCertificateAuthorityRestController.java @@ -19,17 +19,17 @@ @RestController @RequestMapping("/HIRS_AttestationCA") public class AttestationCertificateAuthorityRestController { - private final AttestationCertificateAuthorityServiceImpl attestationCertificateAuthorityServiceImpl; + private final AttestationCertificateAuthorityService attestationCertificateAuthorityService; /** * Constructor. * - * @param attestationCertificateAuthorityServiceImpl Attestation Certificate Authority service + * @param attestationCertificateAuthorityService Attestation Certificate Authority service */ @Autowired public AttestationCertificateAuthorityRestController( - final AttestationCertificateAuthorityServiceImpl attestationCertificateAuthorityServiceImpl) { - this.attestationCertificateAuthorityServiceImpl = attestationCertificateAuthorityServiceImpl; + final AttestationCertificateAuthorityService attestationCertificateAuthorityService) { + this.attestationCertificateAuthorityService = attestationCertificateAuthorityService; } /** @@ -42,7 +42,7 @@ public AttestationCertificateAuthorityRestController( @ResponseBody @PostMapping(value = "/identity-claim-tpm2/process", consumes = MediaType.APPLICATION_OCTET_STREAM_VALUE) public byte[] processIdentityClaimTpm2(@RequestBody final byte[] identityClaim) throws GeneralSecurityException { - return this.attestationCertificateAuthorityServiceImpl.processIdentityClaimTpm2(identityClaim); + return this.attestationCertificateAuthorityService.processIdentityClaimTpm2(identityClaim); } /** @@ -57,7 +57,7 @@ public byte[] processIdentityClaimTpm2(@RequestBody final byte[] identityClaim) @PostMapping(value = "/request-certificate-tpm2", consumes = MediaType.APPLICATION_OCTET_STREAM_VALUE) public byte[] processCertificateRequest(@RequestBody final byte[] certificateRequest) throws GeneralSecurityException { - return this.attestationCertificateAuthorityServiceImpl.processCertificateRequest(certificateRequest); + return this.attestationCertificateAuthorityService.processCertificateRequest(certificateRequest); } /** @@ -68,6 +68,6 @@ public byte[] processCertificateRequest(@RequestBody final byte[] certificateReq @ResponseBody @GetMapping("/public-key") public byte[] getLeafACACertPublicKey() { - return this.attestationCertificateAuthorityServiceImpl.getLeafACACertPublicKey(); + return this.attestationCertificateAuthorityService.getLeafACACertPublicKey(); } } diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/AttestationCertificateAuthorityService.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/AttestationCertificateAuthorityService.java index 54a40c9ba..81727a0cd 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/AttestationCertificateAuthorityService.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/AttestationCertificateAuthorityService.java @@ -13,7 +13,7 @@ public interface AttestationCertificateAuthorityService { * @param identityClaim a byte array representation of the identity claim * @return a byte array representation of the identity claim response */ - byte[] processIdentityClaimTpm2(byte[] identityClaim) throws GeneralSecurityException; + byte[] processIdentityClaimTpm2(byte[] identityClaim); /** * Processes the provided certificate request. @@ -23,4 +23,11 @@ public interface AttestationCertificateAuthorityService { */ byte[] processCertificateRequest(byte[] certificateRequest) throws GeneralSecurityException; + + /** + * Retrieves the encoded public key of the leaf certificate. + * + * @return encoded public key of the leaf certificate + */ + byte[] getLeafACACertPublicKey(); } diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/AttestationCertificateAuthorityServiceImpl.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/AttestationCertificateAuthorityServiceImpl.java index 6b77a05d4..5ccab1685 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/AttestationCertificateAuthorityServiceImpl.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/AttestationCertificateAuthorityServiceImpl.java @@ -3,6 +3,7 @@ import hirs.attestationca.persist.provision.CertificateRequestProcessor; import hirs.attestationca.persist.provision.IdentityClaimProcessor; import lombok.extern.log4j.Log4j2; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.security.GeneralSecurityException; @@ -22,6 +23,7 @@ public class AttestationCertificateAuthorityServiceImpl implements AttestationCe * @param certificateRequestProcessor certificate request processor service * @param identityClaimProcessor identity claim processor service */ + @Autowired public AttestationCertificateAuthorityServiceImpl( final CertificateRequestProcessor certificateRequestProcessor, final IdentityClaimProcessor identityClaimProcessor) { @@ -35,7 +37,7 @@ public AttestationCertificateAuthorityServiceImpl( * @param identityClaim a byte array representation of the identity claim * @return processed identity claim response */ - public byte[] processIdentityClaimTpm2(final byte[] identityClaim) throws GeneralSecurityException { + public byte[] processIdentityClaimTpm2(final byte[] identityClaim) { return this.identityClaimProcessor.processIdentityClaimTpm2(identityClaim); } @@ -50,9 +52,9 @@ public byte[] processCertificateRequest(final byte[] certificateRequest) throws } /** - * Retrieves the encoded public key. + * Retrieves the encoded public key of the leaf certificate. * - * @return encoded public key + * @return encoded public key of the leaf certificate */ public byte[] getLeafACACertPublicKey() { return this.certificateRequestProcessor.getLeafACACertificatePublicKey(); diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/exceptions/CertificateProcessingException.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/exceptions/CertificateProcessingException.java index 84d5eaa1f..49f1d23b1 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/exceptions/CertificateProcessingException.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/exceptions/CertificateProcessingException.java @@ -1,9 +1,7 @@ package hirs.attestationca.persist.exceptions; -import hirs.attestationca.persist.AttestationCertificateAuthorityServiceImpl; - /** - * Generic exception thrown while a {@link AttestationCertificateAuthorityServiceImpl} + * Generic exception thrown when the the Attestation Certificate Authority Service * is processing a newly created Attestation Certificate for a validated identity. */ public class CertificateProcessingException extends RuntimeException { diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/exceptions/IdentityProcessingException.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/exceptions/IdentityProcessingException.java index 5b6cc5eec..27efa4728 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/exceptions/IdentityProcessingException.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/exceptions/IdentityProcessingException.java @@ -1,9 +1,7 @@ package hirs.attestationca.persist.exceptions; -import hirs.attestationca.persist.AttestationCertificateAuthorityServiceImpl; - /** - * Generic exception thrown while a {@link AttestationCertificateAuthorityServiceImpl} + * Generic exception thrown when the Attestation Certificate Authority Service * is processing a newly submitted Identity. */ public class IdentityProcessingException extends RuntimeException { diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/exceptions/UnexpectedServerException.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/exceptions/UnexpectedServerException.java index 890f6c2af..59dbe2102 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/exceptions/UnexpectedServerException.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/exceptions/UnexpectedServerException.java @@ -1,9 +1,7 @@ package hirs.attestationca.persist.exceptions; -import hirs.attestationca.persist.AttestationCertificateAuthorityServiceImpl; - /** - * Generic exception thrown when a {@link AttestationCertificateAuthorityServiceImpl} + * Generic exception thrown when the Attestation Certificate Authority Service * encounters an unexpected condition that can't be handled. */ public class UnexpectedServerException extends RuntimeException { 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 309a299d3..bf013c0bf 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 @@ -37,7 +37,6 @@ @Service @Log4j2 public class CertificateRequestProcessor extends AbstractProcessor { - private final SupplyChainValidationService supplyChainValidationService; private final CertificateRepository certificateRepository; private final DeviceRepository deviceRepository; @@ -53,7 +52,7 @@ public class CertificateRequestProcessor extends AbstractProcessor { * @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 certificateValidityInDays int for the time in which a certificate is valid. * @param tpm2ProvisionerStateRepository db connector for provisioner state. * @param policyRepository db connector for policies. */ @@ -64,10 +63,10 @@ public CertificateRequestProcessor(final SupplyChainValidationService supplyChai final PrivateKey privateKey, @Qualifier("leafACACert") final X509Certificate acaCertificate, @Value("${aca.current.public.key.algorithm}") final String publicKeyAlgorithmStr, - @Value("${aca.certificates.validity}") final int validDays, + @Value("${aca.certificates.validity}") final int certificateValidityInDays, final TPM2ProvisionerStateRepository tpm2ProvisionerStateRepository, final PolicyRepository policyRepository) { - super(privateKey, validDays); + super(privateKey, certificateValidityInDays); this.supplyChainValidationService = supplyChainValidationService; this.certificateRepository = certificateRepository; this.deviceRepository = deviceRepository; diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/IdentityClaimProcessor.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/IdentityClaimProcessor.java index 89272bca6..3dcaa6c46 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/IdentityClaimProcessor.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/IdentityClaimProcessor.java @@ -144,9 +144,9 @@ public byte[] processIdentityClaimTpm2(final byte[] identityClaim) { log.info("Identity Claim has been received and is ready to be processed"); if (ArrayUtils.isEmpty(identityClaim)) { - log.error("Identity claim empty throwing exception."); - throw new IllegalArgumentException("The IdentityClaim sent by the client" - + " cannot be null or empty."); + final String errorMsg = "The IdentityClaim sent by the client cannot be null or empty."; + log.error(errorMsg); + throw new IllegalArgumentException(errorMsg); } final PolicyRepository policyRepository = this.getPolicyRepository(); diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/helper/ProvisionUtils.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/helper/ProvisionUtils.java index 4080e2234..65cf49d6c 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/helper/ProvisionUtils.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/helper/ProvisionUtils.java @@ -4,19 +4,11 @@ import com.google.protobuf.InvalidProtocolBufferException; import hirs.attestationca.configuration.provisionerTpm2.ProvisionerTpm2; import hirs.attestationca.persist.enums.PublicKeyAlgorithm; -import hirs.attestationca.persist.exceptions.CertificateProcessingException; import hirs.attestationca.persist.exceptions.IdentityProcessingException; import hirs.attestationca.persist.exceptions.UnexpectedServerException; -import hirs.structs.converters.SimpleStructBuilder; -import hirs.structs.elements.aca.SymmetricAttestation; -import hirs.structs.elements.tpm.EncryptionScheme; -import hirs.structs.elements.tpm.IdentityRequest; -import hirs.structs.elements.tpm.SymmetricKey; -import hirs.structs.elements.tpm.SymmetricKeyParams; import hirs.utils.HexUtils; import lombok.extern.log4j.Log4j2; import org.apache.commons.codec.binary.Hex; -import org.apache.commons.lang3.ArrayUtils; import org.bouncycastle.openssl.jcajce.JcaPEMWriter; import javax.crypto.BadPaddingException; @@ -39,7 +31,6 @@ import java.security.KeyFactory; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; -import java.security.PrivateKey; import java.security.PublicKey; import java.security.SecureRandom; import java.security.cert.CertificateEncodingException; @@ -266,97 +257,6 @@ public static PublicKey assembleECCPublicKey() { } } - /** - * Will attempt to decrypt the asymmetric blob that originated from an - * {@link IdentityRequest} using the cipher transformation. - * - * @param asymmetricBlob to be decrypted - * @param scheme to decrypt with - * @param privateKey cipher private key - * @return decrypted blob - */ - public static byte[] decryptAsymmetricBlob(final byte[] asymmetricBlob, - final EncryptionScheme scheme, - final PrivateKey privateKey) { - try { - // create a cipher from the specified transformation - Cipher cipher = Cipher.getInstance(scheme.toString()); - - if (scheme == EncryptionScheme.OAEP) { - OAEPParameterSpec spec = - new OAEPParameterSpec("Sha1", "MGF1", MGF1ParameterSpec.SHA1, - new PSource.PSpecified("".getBytes(StandardCharsets.UTF_8))); - - cipher.init(Cipher.PRIVATE_KEY, privateKey, spec); - } else { - // initialize the cipher to decrypt using the ACA private key. - cipher.init(Cipher.DECRYPT_MODE, privateKey); - } - - cipher.update(asymmetricBlob); - - return cipher.doFinal(); - } catch (NoSuchPaddingException | NoSuchAlgorithmException | InvalidKeyException - | BadPaddingException | IllegalBlockSizeException - | InvalidAlgorithmParameterException e) { - throw new IdentityProcessingException( - "Encountered error while decrypting asymmetric blob of an identity request: " - + e.getMessage(), e); - } - } - - /** - * Will attempt to decrypt the symmetric blob that originated from an - * {@link IdentityRequest} using the specified symmetric key - * and cipher transformation. - * - * @param symmetricBlob to be decrypted - * @param symmetricKey to use to decrypt - * @param iv to use with decryption cipher - * @param transformation of the cipher - * @return decrypted symmetric blob - */ - public static byte[] decryptSymmetricBlob(final byte[] symmetricBlob, final byte[] symmetricKey, - final byte[] iv, final String transformation) { - try { - // create a cipher from the specified transformation - Cipher cipher = Cipher.getInstance(transformation); - - // generate a key specification to initialize the cipher - SecretKeySpec keySpec = new SecretKeySpec(symmetricKey, "AES"); - - // initialize the cipher to decrypt using the symmetric key - cipher.init(Cipher.DECRYPT_MODE, keySpec, new IvParameterSpec(iv)); - - // decrypt the symmetric blob - return cipher.doFinal(symmetricBlob); - } catch (IllegalBlockSizeException | InvalidKeyException | NoSuchAlgorithmException - | BadPaddingException | NoSuchPaddingException - | InvalidAlgorithmParameterException exception) { - log.error("Encountered error while decrypting symmetric blob of an identity request: {}", - exception.getMessage(), exception); - } - - return new byte[0]; - } - - /** - * Generates a symmetric key. - * - * @return a symmetric key - */ - public static SymmetricKey generateSymmetricKey() { - // create a session key for the CA contents - byte[] responseSymmetricKey = - generateRandomBytes(DEFAULT_IV_SIZE); - - // create a symmetric key struct for the CA contents - return new SimpleStructBuilder<>(SymmetricKey.class) - .set("algorithmId", SymmetricKey.ALGORITHM_AES) - .set("encryptionScheme", SymmetricKey.SCHEME_CBC) - .set("key", responseSymmetricKey).build(); - } - /** * Performs the first step of the TPM 2.0 identity claim process. Takes an ek, ak, and secret * and then generates a seed that is used to generate AES and HMAC keys. Parses the ak name. @@ -403,11 +303,11 @@ public static ByteString tpm20MakeCredential(final RSAPublicKey ek, final RSAPub startpos = 1; } System.arraycopy(akModTemp, startpos, akMod, 0, DEFAULT_RSA_MODULUS_LENGTH_IN_BYTES); - byte[] akName = ProvisionUtils.generateAkName(akMod); + byte[] akName = generateAkName(akMod); // generate AES and HMAC keys from seed - byte[] aesKey = ProvisionUtils.cryptKDFa(seed, "STORAGE", akName, AES_KEY_LENGTH_BYTES); - byte[] hmacKey = ProvisionUtils.cryptKDFa(seed, "INTEGRITY", null, HMAC_KEY_LENGTH_BYTES); + byte[] aesKey = cryptKDFa(seed, "STORAGE", akName, AES_KEY_LENGTH_BYTES); + byte[] hmacKey = cryptKDFa(seed, "INTEGRITY", null, HMAC_KEY_LENGTH_BYTES); // use two bytes to add a size prefix on secret ByteBuffer b; @@ -439,7 +339,7 @@ public static ByteString tpm20MakeCredential(final RSAPublicKey ek, final RSAPub byte[] topSize = b.array(); // return ordered blob of assembled credentials - byte[] bytesToReturn = ProvisionUtils.assembleCredential(topSize, integrity, encSecret, encSeed); + byte[] bytesToReturn = assembleCredential(topSize, integrity, encSecret, encSeed); return ByteString.copyFrom(bytesToReturn); } catch (BadPaddingException | IllegalBlockSizeException | NoSuchAlgorithmException @@ -451,99 +351,6 @@ public static ByteString tpm20MakeCredential(final RSAPublicKey ek, final RSAPub } } - /** - * Generate asymmetric contents part of the identity response. - * - * @param identityKey identity requests symmetric contents, otherwise, the identity proof - * @param sessionKey identity response session key - * @param publicKey of the EK certificate contained within the identity proof - * @return encrypted asymmetric contents - */ - public static byte[] generateAsymmetricContents(final byte[] identityKey, - final byte[] sessionKey, - final PublicKey publicKey) { - try { - // create a SHA1 digest of the identity key - MessageDigest md = MessageDigest.getInstance("SHA-1"); - md.update(identityKey); - - // generate the digest - byte[] identityDigest = md.digest(); - - // combine the session key with the digest of the identity key - byte[] asymmetricContents = ArrayUtils.addAll(sessionKey, identityDigest); - - // encrypt the asymmetric contents and return - OAEPParameterSpec oaepSpec = - new OAEPParameterSpec("Sha1", "MGF1", MGF1ParameterSpec.SHA1, - new PSource.PSpecified("TCPA".getBytes(StandardCharsets.UTF_8))); - - // initialize the asymmetric cipher using the default OAEP transformation - Cipher cipher = Cipher.getInstance(EncryptionScheme.OAEP.toString()); - - // initialize the cipher using the public spec with the additional OAEP specification - cipher.init(Cipher.PUBLIC_KEY, publicKey, oaepSpec); - - return cipher.doFinal(asymmetricContents); - } catch (NoSuchAlgorithmException | IllegalBlockSizeException | NoSuchPaddingException - | InvalidKeyException | BadPaddingException - | InvalidAlgorithmParameterException e) { - throw new CertificateProcessingException( - "Encountered error while generating ACA session key: " + e.getMessage(), e); - } - } - - /** - * Generate the Identity Response using the identity credential and the session key. - * - * @param credential the identity credential - * @param symmetricKey generated session key for this request/response chain - * @return identity response for an identity request - */ - public static SymmetricAttestation generateAttestation(final X509Certificate credential, - final SymmetricKey symmetricKey) { - try { - // initialize the symmetric cipher - Cipher aesCipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); - - // generate a key specification to initialize the cipher - SecretKeySpec keySpec = new SecretKeySpec(symmetricKey.getKey(), "AES"); - - // fill IV with random bytes - byte[] credentialIV = generateRandomBytes(DEFAULT_IV_SIZE); - - // create IV encryption parameter specification - IvParameterSpec ivParameterSpec = new IvParameterSpec(credentialIV); - - // initialize the cipher to decrypt using the symmetric key - aesCipher.init(Cipher.ENCRYPT_MODE, keySpec, ivParameterSpec); - - // encrypt the credential - byte[] encryptedCredential = aesCipher.doFinal(credential.getEncoded()); - - // prepend the IV to the encrypted credential - byte[] credentialBytes = ArrayUtils.addAll(credentialIV, encryptedCredential); - - // create attestation for identity response that contains the credential - - return new SimpleStructBuilder<>(SymmetricAttestation.class) - .set("credential", credentialBytes) - .set("algorithm", - new SimpleStructBuilder<>(SymmetricKeyParams.class) - .set("algorithmId", SymmetricKeyParams.ALGORITHM_AES) - .set("encryptionScheme", - SymmetricKeyParams.SCHEME_CBC_PKCS5PADDING) - .set("signatureScheme", 0).build()).build(); - - } catch (BadPaddingException | IllegalBlockSizeException | NoSuchAlgorithmException - | InvalidKeyException | InvalidAlgorithmParameterException | NoSuchPaddingException - | CertificateEncodingException exception) { - throw new CertificateProcessingException( - "Encountered error while generating Identity Response: " - + exception.getMessage(), exception); - } - } - /** * Assembles a credential blob. * diff --git a/HIRS_AttestationCA/src/test/java/hirs/attestationca/persist/AttestationCertificateAuthorityServiceTest.java b/HIRS_AttestationCA/src/test/java/hirs/attestationca/persist/AttestationCertificateAuthorityServiceTest.java new file mode 100644 index 000000000..e138eddd3 --- /dev/null +++ b/HIRS_AttestationCA/src/test/java/hirs/attestationca/persist/AttestationCertificateAuthorityServiceTest.java @@ -0,0 +1,468 @@ +package hirs.attestationca.persist; + +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.CertificateRequestProcessor; +import hirs.attestationca.persist.provision.IdentityClaimProcessor; +import hirs.attestationca.persist.provision.helper.IssuedCertificateAttributeHelper; +import hirs.attestationca.persist.provision.helper.ProvisionUtils; +import hirs.structs.elements.tpm.AsymmetricPublicKey; +import hirs.structs.elements.tpm.IdentityProof; +import hirs.structs.elements.tpm.StorePubKey; +import hirs.utils.HexUtils; +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.cert.X509v3CertificateBuilder; +import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; +import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.operator.ContentSigner; +import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.springframework.test.util.ReflectionTestUtils; + +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.GeneralSecurityException; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.Security; +import java.security.cert.X509Certificate; +import java.security.interfaces.RSAPublicKey; +import java.util.Calendar; +import java.util.Date; +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.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + +/** + * + */ +public class AttestationCertificateAuthorityServiceTest { + 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 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"; + + private AutoCloseable mocks; + + private KeyPair keyPair; + + @InjectMocks + private AttestationCertificateAuthorityServiceImpl attestationCertificateAuthorityService; + + private AccessAbstractProcessor abstractProcessor; + + @Mock + private CertificateRequestProcessor certificateRequestProcessor; + + @Mock + private IdentityClaimProcessor identityClaimProcessor; + + @BeforeEach + public void setupTests() throws NoSuchAlgorithmException { + // Initializes mocks before each test + mocks = MockitoAnnotations.openMocks(this); + + final int keySize = 2048; + KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); + keyPairGenerator.initialize(keySize); + keyPair = keyPairGenerator.generateKeyPair(); + } + + @AfterEach + public void afterEach() throws Exception { + if (mocks != null) { + mocks.close(); + } + } + + /** + * Tests {@link AttestationCertificateAuthorityService#processIdentityClaimTpm2(byte[])} + * where the byte array is null or empty. Expects an illegal argument exception to be thrown. + * + * @throws GeneralSecurityException if any issues arise while processing the identity claim request + */ + @Test + public void testProcessIdentityClaimTpm2NullOrEmptyRequest() throws GeneralSecurityException { + // test 1: test null identity claim + when(attestationCertificateAuthorityService.processIdentityClaimTpm2(null)).thenThrow( + new IllegalArgumentException("The IdentityClaim sent by the client cannot be null or empty.")); + + // Act & Assert: Verify that the exception is thrown + IllegalArgumentException illegalArgumentException = assertThrows(IllegalArgumentException.class, + () -> attestationCertificateAuthorityService.processIdentityClaimTpm2(null)); + + assertEquals("The IdentityClaim sent by the client cannot be null or empty.", + illegalArgumentException.getMessage()); + + // test 2: test empty identity claim + + // initialize an empty byte array + final byte[] emptyArr = {}; + + when(attestationCertificateAuthorityService.processIdentityClaimTpm2(emptyArr)).thenThrow( + new IllegalArgumentException("The IdentityClaim sent by the client cannot be null or empty.")); + + // Act & Assert: Verify that the exception is thrown + illegalArgumentException = assertThrows(IllegalArgumentException.class, + () -> attestationCertificateAuthorityService.processIdentityClaimTpm2(emptyArr)); + + assertEquals("The IdentityClaim sent by the client cannot be null or empty.", + illegalArgumentException.getMessage()); + } + + /** + * Tests {@link AttestationCertificateAuthorityService#processCertificateRequest(byte[])} + * where the byte array is null or empty. Expects an illegal argument exception to be thrown. + * + * @throws GeneralSecurityException if any issues arise while processing the certificate request + */ + @Test + public void testProcessCertificateRequestNullOrEmptyRequest() throws GeneralSecurityException { + // test 1: test null certificate request + + when(attestationCertificateAuthorityService.processCertificateRequest(null)).thenThrow( + new IllegalArgumentException("The CertificateRequest sent by the client cannot be null or empty.")); + + // Act & Assert: Verify that the exception is thrown + IllegalArgumentException illegalArgumentException = assertThrows(IllegalArgumentException.class, + () -> attestationCertificateAuthorityService.processCertificateRequest(null)); + + assertEquals("The CertificateRequest sent by the client cannot be null or empty.", + illegalArgumentException.getMessage()); + + // test 2: test empty certificate request + + // initialize an empty byte array + final byte[] emptyArr = {}; + + when(attestationCertificateAuthorityService.processCertificateRequest(emptyArr)).thenThrow( + new IllegalArgumentException("The CertificateRequest sent by the client cannot be null or empty.")); + + illegalArgumentException = assertThrows(IllegalArgumentException.class, + () -> attestationCertificateAuthorityService.processCertificateRequest(emptyArr)); + + assertEquals("The CertificateRequest sent by the client cannot be null or empty.", + illegalArgumentException.getMessage()); + } + + /** + * Tests {@link AttestationCertificateAuthorityService#getLeafACACertPublicKey()}. + */ + @Test + public void testGetPublicKey() { + + // setup + + // encoded byte array to be returned by public key + final byte[] desiredByteArray = new byte[]{0, 1, 0, 1, 0}; + + // create mocks for testing + X509Certificate mockCertificate = mock(X509Certificate.class); + PublicKey mockPublicKey = mock(PublicKey.class); + + // Mock the behavior of getPublicKey().getEncoded() to return the desired byte array + when(mockCertificate.getPublicKey()).thenReturn(mockPublicKey); + when(mockPublicKey.getEncoded()).thenReturn(desiredByteArray); + + when(attestationCertificateAuthorityService.getLeafACACertPublicKey()).thenReturn( + mockCertificate.getPublicKey().getEncoded()); + + // Test: Call the service method and assert the return value + byte[] actualByteArray = attestationCertificateAuthorityService.getLeafACACertPublicKey(); + + // Assert that the byte arrays match + assertArrayEquals(desiredByteArray, actualByteArray); + } + + /** + * Tests {@link ProvisionUtils#assembleRSAPublicKey(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.assembleRSAPublicKey(modulus.toByteArray()); + + // assert that the exponent and the modulus are the same. the exponents should be the well + // known prime, 101 + final int radix = 16; + assertEquals(new BigInteger("010001", radix), publicKey.getPublicExponent()); + assertEquals(publicKey.getModulus(), 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(Objects.requireNonNull(getClass().getResource(EK_PUBLIC_PATH)).toURI()); + + byte[] ekFile = Files.readAllBytes(ekPath); + + RSAPublicKey ek = ProvisionUtils.parseRSAKeyFromPublicDataSegment(ekFile); + final int radix = 16; + assertEquals(new BigInteger("010001", radix), ek.getPublicExponent()); + + 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(Objects.requireNonNull(getClass().getResource(AK_PUBLIC_PATH)).toURI()); + + byte[] akFile = Files.readAllBytes(akPath); + + RSAPublicKey ak = ProvisionUtils.parseRSAKeyFromPublicDataSegment(akFile); + final int radix = 16; + assertEquals(new BigInteger("010001", radix), ak.getPublicExponent()); + + 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 {@link AttestationCertificateAuthorityService# + * 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(); + int validDays = 1; + + // create mocks for testing + IdentityProof identityProof = mock(IdentityProof.class); + AsymmetricPublicKey asymmetricPublicKey = mock(AsymmetricPublicKey.class); + StorePubKey storePubKey = mock(StorePubKey.class); + X509Certificate acaCertificate = createSelfSignedCertificate(keyPair); + + // assign ACA fields + ReflectionTestUtils.setField(attestationCertificateAuthorityService, "validDays", validDays); + ReflectionTestUtils.setField(attestationCertificateAuthorityService, "acaCertificate", acaCertificate); + + // prepare identity proof interactions + when(identityProof.getLabel()).thenReturn(identityProofLabel); + + // perform the test + X509Certificate certificate = abstractProcessor.accessGenerateCredential(keyPair.getPublic(), + null, + new LinkedList<>(), + "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 + verifyNoMoreInteractions(identityProof, asymmetricPublicKey, storePubKey); + } + + /** + * Creates a self-signed X.509 public-key certificate. + * + * @param pair KeyPair to create the cert for + * @return self-signed X509Certificate + */ + private X509Certificate createSelfSignedCertificate(final KeyPair pair) { + Security.addProvider(new BouncyCastleProvider()); + final int timeRange = 10000; + X509Certificate cert = null; + try { + X500Name issuerName = new X500Name("CN=TEST2, OU=TEST2, O=TEST2, C=TEST2"); + X500Name subjectName = new X500Name("CN=TEST, OU=TEST, O=TEST, C=TEST"); + BigInteger serialNumber = BigInteger.ONE; + Date notBefore = new Date(System.currentTimeMillis() - timeRange); + Date notAfter = new Date(System.currentTimeMillis() + timeRange); + X509v3CertificateBuilder builder = + new JcaX509v3CertificateBuilder(issuerName, serialNumber, notBefore, notAfter, + subjectName, pair.getPublic()); + ContentSigner signer = + new JcaContentSignerBuilder("SHA256WithRSA").setProvider("BC").build( + pair.getPrivate()); + return new JcaX509CertificateConverter().setProvider("BC").getCertificate( + builder.build(signer)); + } catch (Exception e) { + fail("Exception occurred while creating a cert", e); + } + return cert; + } + + + /** + * 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 platformCredentials, + final String deviceName, + final X509Certificate acaCertificate) { + + return generateCredential(publicKey, + endorsementCredential, + platformCredentials, + deviceName, + acaCertificate); + } + } + +} diff --git a/HIRS_AttestationCA/src/test/java/hirs/attestationca/persist/AttestationCertificateAuthorityTest.java b/HIRS_AttestationCA/src/test/java/hirs/attestationca/persist/AttestationCertificateAuthorityTest.java deleted file mode 100644 index 8bebef547..000000000 --- a/HIRS_AttestationCA/src/test/java/hirs/attestationca/persist/AttestationCertificateAuthorityTest.java +++ /dev/null @@ -1,795 +0,0 @@ -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.cert.X509v3CertificateBuilder; -import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; -import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder; -import org.bouncycastle.jce.provider.BouncyCastleProvider; -import org.bouncycastle.operator.ContentSigner; -import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; -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 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.Security; -import java.security.cert.X509Certificate; -import java.security.interfaces.RSAPublicKey; -import java.security.spec.MGF1ParameterSpec; -import java.util.Calendar; -import java.util.Date; -import java.util.LinkedList; -import java.util.List; -import java.util.Objects; -import java.util.Random; - -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.junit.jupiter.api.Assertions.fail; -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 AttestationCertificateAuthorityServiceImpl}. - */ -@TestInstance(TestInstance.Lifecycle.PER_CLASS) // needed to use non-static BeforeAll -public class AttestationCertificateAuthorityTest { - - // 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"; - private final Random random = new Random(); - // object in test - private AttestationCertificateAuthorityServiceImpl aca; - private AccessAbstractProcessor abstractProcessor; - // test key pair - private KeyPair keyPair; - - /** - * Creates a self-signed X.509 public-key certificate. - * - * @param pair KeyPair to create the cert for - * @return self-signed X509Certificate - */ - private static X509Certificate createSelfSignedCertificate(final KeyPair pair) { - Security.addProvider(new BouncyCastleProvider()); - final int timeRange = 10000; - X509Certificate cert = null; - try { - - X500Name issuerName = new X500Name("CN=TEST2, OU=TEST2, O=TEST2, C=TEST2"); - X500Name subjectName = new X500Name("CN=TEST, OU=TEST, O=TEST, C=TEST"); - BigInteger serialNumber = BigInteger.ONE; - Date notBefore = new Date(System.currentTimeMillis() - timeRange); - Date notAfter = new Date(System.currentTimeMillis() + timeRange); - X509v3CertificateBuilder builder = - new JcaX509v3CertificateBuilder(issuerName, serialNumber, notBefore, notAfter, - subjectName, pair.getPublic()); - ContentSigner signer = - new JcaContentSignerBuilder("SHA256WithRSA").setProvider("BC").build( - pair.getPrivate()); - return new JcaX509CertificateConverter().setProvider("BC").getCertificate( - builder.build(signer)); - } catch (Exception e) { - fail("Exception occurred while creating a cert", e); - } - return cert; - } - - /** - * 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 - final int keySize = 2048; - KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); - keyPairGenerator.initialize(keySize); - keyPair = keyPairGenerator.generateKeyPair(); - - //BeforeTest - aca = new AttestationCertificateAuthorityServiceImpl(null, - null) { - }; - abstractProcessor = new AccessAbstractProcessor(keyPair.getPrivate(), 1); - - Security.addProvider(new BouncyCastleProvider()); - } - - /** - * Tests {@link AttestationCertificateAuthorityServiceImpl#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 AttestationCertificateAuthorityServiceImpl#getLeafACACertPublicKey()}. - */ - @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.getLeafACACertPublicKey()); - - // 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 - 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 - final int expectedAlgorithmId = 6; - final int expectedEncryptionScheme = 255; - - assertTrue(symmetricKey.getAlgorithmId() == expectedAlgorithmId); - assertTrue(symmetricKey.getEncryptionScheme() == expectedEncryptionScheme); - 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]; - - 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 - final int expectedAlgorithmId = 6; - assertNotNull(attestation.getAlgorithm()); - assertTrue(attestation.getAlgorithm().getAlgorithmId() == expectedAlgorithmId); - 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 AttestationCertificateAuthorityServiceImpl# - * 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(); - int validDays = 1; - - // create mocks for testing - IdentityProof identityProof = mock(IdentityProof.class); - AsymmetricPublicKey asymmetricPublicKey = mock(AsymmetricPublicKey.class); - StorePubKey storePubKey = mock(StorePubKey.class); - X509Certificate acaCertificate = createSelfSignedCertificate(keyPair); - - // assign ACA fields - ReflectionTestUtils.setField(aca, "validDays", validDays); - ReflectionTestUtils.setField(aca, "acaCertificate", acaCertificate); - - // prepare identity proof interactions - when(identityProof.getLabel()).thenReturn(identityProofLabel); - - // perform the test - X509Certificate certificate = abstractProcessor.accessGenerateCredential(keyPair.getPublic(), - null, - new LinkedList(), - "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 - verifyNoMoreInteractions(identityProof, asymmetricPublicKey, storePubKey); - } - - /** - * Tests {@link ProvisionUtils#assembleRSAPublicKey(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.assembleRSAPublicKey(modulus.toByteArray()); - - // assert that the exponent and the modulus are the same. the exponents should be the well - // known prime, 101 - final int radix = 16; - assertTrue(publicKey.getPublicExponent().equals(new BigInteger("010001", radix))); - assertTrue(publicKey.getModulus().equals(modulus)); - } - - /** - * Tests {@link ProvisionUtils#assembleRSAPublicKey(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.assembleRSAPublicKey(modulusString); - - // assert that the exponent and the modulus are the same. the exponents should be the well - // known prime, 101. - final int radix = 16; - assertTrue(publicKey.getPublicExponent().equals(new BigInteger("010001", radix))); - 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.parseRSAKeyFromPublicDataSegment(ekFile); - final int radix = 16; - assertTrue(ek.getPublicExponent().equals(new BigInteger("010001", radix))); - - 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.parseRSAKeyFromPublicDataSegment(akFile); - final int radix = 16; - assertTrue(ak.getPublicExponent().equals(new BigInteger("010001", radix))); - - 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.parseRSAKeyFromPublicDataSegment(ekPubFile); - RSAPublicKey akPub = ProvisionUtils.parseRSAKeyFromPublicDataSegment(akPubFile); - - // prepare the nonce and wrap it with keys - final 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 blob to be encrypted - * @param key shared key - * @param iv to encrypt with - * @param transformation of the encryption cipher - * @return encrypted blob - * @throws Exception if there are any issues while encrypting the blob - */ - 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 blob to be decrypted - * @return decrypted blob - * @throws Exception if there are any issues while decrypting the blob - */ - 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 blob to be decrypted - * @param key shared key - * @param iv to decrypt with - * @param transformation of the decryption cipher - * @return decrypted blob - * @throws Exception if there are any issues while decrypting the blob - */ - 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); - } - - /** - * 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 platformCredentials, - final String deviceName, - final X509Certificate acaCertificate) { - - return generateCredential(publicKey, - endorsementCredential, - platformCredentials, - deviceName, - acaCertificate); - } - } -} From 6527bc0e948c06a756facf7bb639d155caa5d581 Mon Sep 17 00:00:00 2001 From: ThatSilentCoder <184309164+ThatSilentCoder@users.noreply.github.com> Date: Wed, 25 Feb 2026 17:55:43 -0500 Subject: [PATCH 06/25] v3.1_issue_1090: SLowly but surely making progress. --- .../persist/enums/PublicKeyAlgorithm.java | 22 +- .../provision/helper/ProvisionUtils.java | 56 ++- ...tationCertificateAuthorityServiceTest.java | 346 ++++++------------ .../resources/{tpm2 => public_keys}/ak.name | Bin .../resources/{tpm2 => public_keys}/ak.pub | Bin .../resources/{tpm2 => public_keys}/ek.pub | Bin 6 files changed, 183 insertions(+), 241 deletions(-) rename HIRS_AttestationCA/src/test/resources/{tpm2 => public_keys}/ak.name (100%) rename HIRS_AttestationCA/src/test/resources/{tpm2 => public_keys}/ak.pub (100%) rename HIRS_AttestationCA/src/test/resources/{tpm2 => public_keys}/ek.pub (100%) diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/enums/PublicKeyAlgorithm.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/enums/PublicKeyAlgorithm.java index a4a8c85ad..c2eef2772 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/enums/PublicKeyAlgorithm.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/enums/PublicKeyAlgorithm.java @@ -24,11 +24,31 @@ @AllArgsConstructor @ToString public enum PublicKeyAlgorithm { + /** + * RSA Public Key Algorithm. + */ RSA("RSA", RSAPublicKey.class), - ECC("ECC", ECPublicKey.class), + + /** + * ECC Public Key Algorithm. + */ + ECC("EC", ECPublicKey.class), + + /** + * Represents an unknown public key algorithm. + * This is used when the application encounters a public key algorithm that is not recognized or supported. + * It may occur if the algorithm is unsupported or not implemented in the current version of the application. + */ UNKNOWN("UNKNOWN", null); + /** + * The name of the cryptographic algorithm. + */ private final String algorithmName; + + /** + * The Java Security class corresponding to the public key type used by the algorithm. + */ private final Class keyClass; /** diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/helper/ProvisionUtils.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/helper/ProvisionUtils.java index 65cf49d6c..89e0e0e49 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/helper/ProvisionUtils.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/helper/ProvisionUtils.java @@ -37,7 +37,10 @@ import java.security.cert.X509Certificate; import java.security.interfaces.ECPublicKey; import java.security.interfaces.RSAPublicKey; +import java.security.spec.ECParameterSpec; +import java.security.spec.ECPoint; import java.security.spec.ECPublicKeySpec; +import java.security.spec.EllipticCurve; import java.security.spec.InvalidKeySpecException; import java.security.spec.MGF1ParameterSpec; import java.security.spec.RSAPublicKeySpec; @@ -177,18 +180,20 @@ public static PublicKey parsePublicKeyFromPublicDataSegment(final PublicKeyAlgor /** * Parses the RSA public key from public data segment generated by TPM 2.0. * - * @param publicArea the public area segment to parse + * @param publicAreaSegment the public area segment to parse * @return the RSA public key of the supplied public data */ - public static RSAPublicKey parseRSAKeyFromPublicDataSegment(final byte[] publicArea) { - final int pubLen = publicArea.length; + public static RSAPublicKey parseRSAKeyFromPublicDataSegment(final byte[] publicAreaSegment) { + final int publicAreaLen = publicAreaSegment.length; - if (pubLen < DEFAULT_RSA_MODULUS_LENGTH_IN_BYTES) { + if (publicAreaLen < DEFAULT_RSA_MODULUS_LENGTH_IN_BYTES) { throw new IllegalArgumentException("EK or AK public data segment is not long enough"); } // public data ends with 256 byte modulus - byte[] modulus = HexUtils.subarray(publicArea, pubLen - DEFAULT_RSA_MODULUS_LENGTH_IN_BYTES, pubLen - 1); + byte[] modulus = + HexUtils.subarray(publicAreaSegment, publicAreaLen - DEFAULT_RSA_MODULUS_LENGTH_IN_BYTES, + publicAreaLen - 1); return (RSAPublicKey) assembleRSAPublicKey(modulus); } @@ -233,21 +238,48 @@ public static PublicKey assembleRSAPublicKey(final BigInteger modulus) { } /** + * todo * Parses the ECC public key from public data segment generated by TPM 2.0. * - * @param publicArea the public area segment to parse + * @param publicAreaSegment the public area segment to parse * @return the ECC public key of the supplied public data */ - public static ECPublicKey parseECCKeyFromPublicDataSegment(final byte[] publicArea) { - final int pubLen = publicArea.length; + public static ECPublicKey parseECCKeyFromPublicDataSegment(final byte[] publicAreaSegment) { + //final int pubLen = publicArea.length; - return (ECPublicKey) assembleECCPublicKey(); + final BigInteger x = new BigInteger("0"); + final BigInteger y = new BigInteger("0"); + + return assembleECCPublicKey(new ECPoint(x, y)); } - public static PublicKey assembleECCPublicKey() { - ECPublicKeySpec ecPublicKeySpec = null; + /** + * todo + * + * @param ecPoint + * @return + */ + public static ECPublicKey assembleECCPublicKey(final ECPoint ecPoint) { + BigInteger a = new BigInteger("0"); + BigInteger b = new BigInteger("0"); + + EllipticCurve ellipticCurve = new EllipticCurve(null, a, b); + ECParameterSpec ecParameterSpec = null; - // create the RSA public key + return (ECPublicKey) assembleECCPublicKey(ecPoint, ecParameterSpec); + } + + /** + * todo + * + * @param ecpoint + * @param ecParameterSpec + * @return + */ + public static PublicKey assembleECCPublicKey(final ECPoint ecpoint, final ECParameterSpec ecParameterSpec) { + final ECPublicKeySpec ecPublicKeySpec = new ECPublicKeySpec(ecpoint, ecParameterSpec); + + // create the ECC public key try { KeyFactory keyFactory = KeyFactory.getInstance(PublicKeyAlgorithm.ECC.getAlgorithmName()); return keyFactory.generatePublic(ecPublicKeySpec); diff --git a/HIRS_AttestationCA/src/test/java/hirs/attestationca/persist/AttestationCertificateAuthorityServiceTest.java b/HIRS_AttestationCA/src/test/java/hirs/attestationca/persist/AttestationCertificateAuthorityServiceTest.java index e138eddd3..e9cf33945 100644 --- a/HIRS_AttestationCA/src/test/java/hirs/attestationca/persist/AttestationCertificateAuthorityServiceTest.java +++ b/HIRS_AttestationCA/src/test/java/hirs/attestationca/persist/AttestationCertificateAuthorityServiceTest.java @@ -1,39 +1,20 @@ package hirs.attestationca.persist; -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.exceptions.CertificateProcessingException; import hirs.attestationca.persist.provision.CertificateRequestProcessor; import hirs.attestationca.persist.provision.IdentityClaimProcessor; -import hirs.attestationca.persist.provision.helper.IssuedCertificateAttributeHelper; import hirs.attestationca.persist.provision.helper.ProvisionUtils; -import hirs.structs.elements.tpm.AsymmetricPublicKey; -import hirs.structs.elements.tpm.IdentityProof; -import hirs.structs.elements.tpm.StorePubKey; import hirs.utils.HexUtils; -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.cert.X509v3CertificateBuilder; -import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; -import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder; -import org.bouncycastle.jce.provider.BouncyCastleProvider; -import org.bouncycastle.operator.ContentSigner; -import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import org.springframework.test.util.ReflectionTestUtils; 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; @@ -41,35 +22,26 @@ import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.NoSuchAlgorithmException; -import java.security.PrivateKey; import java.security.PublicKey; -import java.security.Security; import java.security.cert.X509Certificate; import java.security.interfaces.RSAPublicKey; -import java.util.Calendar; -import java.util.Date; -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.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; /** - * + * Tests the {@link AttestationCertificateAuthorityServiceImpl} service class. */ public class AttestationCertificateAuthorityServiceTest { - private static final String EK_PUBLIC_PATH = "/tpm2/ek.pub"; + private static final String EK_PUBLIC_KEY_PATH = "/public_keys/ek.pub"; - private static final String AK_PUBLIC_PATH = "/tpm2/ak.pub"; + private static final String AK_PUBLIC_KEY_PATH = "/public_keys/ak.pub"; - private static final String AK_NAME_PATH = "/tpm2/ak.name"; + private static final String AK_NAME_PATH = "/public_keys/ak.name"; 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" @@ -116,14 +88,17 @@ public class AttestationCertificateAuthorityServiceTest { @InjectMocks private AttestationCertificateAuthorityServiceImpl attestationCertificateAuthorityService; - private AccessAbstractProcessor abstractProcessor; - @Mock private CertificateRequestProcessor certificateRequestProcessor; @Mock private IdentityClaimProcessor identityClaimProcessor; + /** + * Setups configuration prior to each test method. + * + * @throws NoSuchAlgorithmException if issues arise while generating keypair. + */ @BeforeEach public void setupTests() throws NoSuchAlgorithmException { // Initializes mocks before each test @@ -135,6 +110,11 @@ public void setupTests() throws NoSuchAlgorithmException { keyPair = keyPairGenerator.generateKeyPair(); } + /** + * Closes mocks after the completion of each test method. + * + * @throws Exception if any issues arise while closing mocks. + */ @AfterEach public void afterEach() throws Exception { if (mocks != null) { @@ -144,84 +124,142 @@ public void afterEach() throws Exception { /** * Tests {@link AttestationCertificateAuthorityService#processIdentityClaimTpm2(byte[])} - * where the byte array is null or empty. Expects an illegal argument exception to be thrown. - * - * @throws GeneralSecurityException if any issues arise while processing the identity claim request + * where the byte array is null or empty. Expects an {@link IllegalArgumentException} to be thrown. */ @Test - public void testProcessIdentityClaimTpm2NullOrEmptyRequest() throws GeneralSecurityException { + public void testProcessIdentityClaimTpm2NullOrEmptyRequest() { + final String expectedExceptionMsg = "The IdentityClaim sent by the client cannot be null or empty."; + // test 1: test null identity claim - when(attestationCertificateAuthorityService.processIdentityClaimTpm2(null)).thenThrow( - new IllegalArgumentException("The IdentityClaim sent by the client cannot be null or empty.")); + when(identityClaimProcessor.processIdentityClaimTpm2(null)).thenThrow( + new IllegalArgumentException(expectedExceptionMsg)); // Act & Assert: Verify that the exception is thrown - IllegalArgumentException illegalArgumentException = assertThrows(IllegalArgumentException.class, - () -> attestationCertificateAuthorityService.processIdentityClaimTpm2(null)); + String actualExceptionMsg = assertThrows(IllegalArgumentException.class, + () -> attestationCertificateAuthorityService.processIdentityClaimTpm2(null)).getMessage(); - assertEquals("The IdentityClaim sent by the client cannot be null or empty.", - illegalArgumentException.getMessage()); + assertEquals(expectedExceptionMsg, actualExceptionMsg); // test 2: test empty identity claim // initialize an empty byte array final byte[] emptyArr = {}; - when(attestationCertificateAuthorityService.processIdentityClaimTpm2(emptyArr)).thenThrow( - new IllegalArgumentException("The IdentityClaim sent by the client cannot be null or empty.")); + when(identityClaimProcessor.processIdentityClaimTpm2(emptyArr)).thenThrow( + new IllegalArgumentException(expectedExceptionMsg)); // Act & Assert: Verify that the exception is thrown - illegalArgumentException = assertThrows(IllegalArgumentException.class, - () -> attestationCertificateAuthorityService.processIdentityClaimTpm2(emptyArr)); + actualExceptionMsg = assertThrows(IllegalArgumentException.class, + () -> attestationCertificateAuthorityService.processIdentityClaimTpm2(emptyArr)).getMessage(); - assertEquals("The IdentityClaim sent by the client cannot be null or empty.", - illegalArgumentException.getMessage()); + assertEquals(expectedExceptionMsg, actualExceptionMsg); + } + + /** + * Tests {@link AttestationCertificateAuthorityService#processIdentityClaimTpm2(byte[])}. + */ + @Test + public void testProcessIdentityClaimTpm2() { + final byte[] identityClaim = {0, 1, 0, 1, 2, 2, 2}; + + final byte[] expectedIdentityClaimResponse = {1, 1, 1, 1, 2, 2, 2, 2}; + + when(identityClaimProcessor.processIdentityClaimTpm2(identityClaim)).thenReturn( + expectedIdentityClaimResponse); + + final byte[] actualCertificateResponse = + identityClaimProcessor.processIdentityClaimTpm2(identityClaim); + + // Assert that the byte arrays match + assertArrayEquals(expectedIdentityClaimResponse, actualCertificateResponse); } /** * Tests {@link AttestationCertificateAuthorityService#processCertificateRequest(byte[])} - * where the byte array is null or empty. Expects an illegal argument exception to be thrown. + * where the byte array is null or empty. Expects an {@link IllegalArgumentException} to be thrown. * * @throws GeneralSecurityException if any issues arise while processing the certificate request */ @Test public void testProcessCertificateRequestNullOrEmptyRequest() throws GeneralSecurityException { - // test 1: test null certificate request + final String expectedExceptionMsg = "The CertificateRequest sent by the client cannot be null or empty."; - when(attestationCertificateAuthorityService.processCertificateRequest(null)).thenThrow( - new IllegalArgumentException("The CertificateRequest sent by the client cannot be null or empty.")); + // test 1: test null certificate request + when(certificateRequestProcessor.processCertificateRequest(null)).thenThrow( + new IllegalArgumentException(expectedExceptionMsg)); // Act & Assert: Verify that the exception is thrown - IllegalArgumentException illegalArgumentException = assertThrows(IllegalArgumentException.class, - () -> attestationCertificateAuthorityService.processCertificateRequest(null)); + String actualExceptionMsg = assertThrows(IllegalArgumentException.class, + () -> attestationCertificateAuthorityService.processCertificateRequest(null)).getMessage(); - assertEquals("The CertificateRequest sent by the client cannot be null or empty.", - illegalArgumentException.getMessage()); + assertEquals(expectedExceptionMsg, actualExceptionMsg); // test 2: test empty certificate request // initialize an empty byte array final byte[] emptyArr = {}; - when(attestationCertificateAuthorityService.processCertificateRequest(emptyArr)).thenThrow( - new IllegalArgumentException("The CertificateRequest sent by the client cannot be null or empty.")); + when(certificateRequestProcessor.processCertificateRequest(emptyArr)).thenThrow( + new IllegalArgumentException(expectedExceptionMsg)); - illegalArgumentException = assertThrows(IllegalArgumentException.class, - () -> attestationCertificateAuthorityService.processCertificateRequest(emptyArr)); + // Act & Assert: Verify that the exception is thrown + actualExceptionMsg = assertThrows(IllegalArgumentException.class, + () -> attestationCertificateAuthorityService.processCertificateRequest(emptyArr)).getMessage(); - assertEquals("The CertificateRequest sent by the client cannot be null or empty.", - illegalArgumentException.getMessage()); + assertEquals(expectedExceptionMsg, actualExceptionMsg); } /** - * Tests {@link AttestationCertificateAuthorityService#getLeafACACertPublicKey()}. + * Tests {@link AttestationCertificateAuthorityService#processCertificateRequest(byte[])} + * where the byte array is invalid. Expects a {@link CertificateProcessingException} to be thrown. + * + * @throws GeneralSecurityException if any issues arise while processing the certificate request */ @Test - public void testGetPublicKey() { + public void testProcessCertificateRequestProcessorDeserializationError() throws GeneralSecurityException { + final String expectedExceptionMsg = "Could not deserialize Protobuf Certificate Request object"; + + final byte[] badCertificateRequest = {0, 0, 0, 0, 0, 1, 0, 0}; + + when(certificateRequestProcessor.processCertificateRequest(badCertificateRequest)).thenThrow( + new CertificateProcessingException(expectedExceptionMsg)); + + // Act & Assert: Verify that the exception is thrown + String actualExceptionMsg = assertThrows(CertificateProcessingException.class, + () -> attestationCertificateAuthorityService.processCertificateRequest( + badCertificateRequest)).getMessage(); + + assertEquals(expectedExceptionMsg, actualExceptionMsg); + } + + /** + * Tests {@link AttestationCertificateAuthorityService#processCertificateRequest(byte[])}. + * + * @throws GeneralSecurityException if any issues arise while processing the certificate request + */ + @Test + public void testProcessCertificateRequest() throws GeneralSecurityException { + final byte[] certificateRequest = {0, 1, 0, 1}; + + final byte[] expectedCertificateResponse = {1, 1, 1, 1}; + + when(certificateRequestProcessor.processCertificateRequest(certificateRequest)).thenReturn( + expectedCertificateResponse); + + final byte[] actualCertificateResponse = + attestationCertificateAuthorityService.processCertificateRequest(certificateRequest); - // setup + // Assert that the byte arrays match + assertArrayEquals(expectedCertificateResponse, actualCertificateResponse); + } + /** + * Tests {@link AttestationCertificateAuthorityService#getLeafACACertPublicKey()}. + */ + @Test + public void testGetPublicKey() { // encoded byte array to be returned by public key - final byte[] desiredByteArray = new byte[]{0, 1, 0, 1, 0}; + final byte[] expectedByteArray = new byte[]{0, 1, 0, 1, 0}; // create mocks for testing X509Certificate mockCertificate = mock(X509Certificate.class); @@ -229,23 +267,26 @@ public void testGetPublicKey() { // Mock the behavior of getPublicKey().getEncoded() to return the desired byte array when(mockCertificate.getPublicKey()).thenReturn(mockPublicKey); - when(mockPublicKey.getEncoded()).thenReturn(desiredByteArray); + when(mockPublicKey.getEncoded()).thenReturn(expectedByteArray); + + // grab the public key encoding + byte[] mockedByteArrayResult = mockPublicKey.getEncoded(); - when(attestationCertificateAuthorityService.getLeafACACertPublicKey()).thenReturn( - mockCertificate.getPublicKey().getEncoded()); + // Mock the behavior of retrieving the public key from the service class + when(attestationCertificateAuthorityService.getLeafACACertPublicKey()).thenReturn(mockedByteArrayResult); // Test: Call the service method and assert the return value byte[] actualByteArray = attestationCertificateAuthorityService.getLeafACACertPublicKey(); - // Assert that the byte arrays match - assertArrayEquals(desiredByteArray, actualByteArray); + // Assert that the mocked and actual byte arrays match + assertArrayEquals(expectedByteArray, actualByteArray); } /** * Tests {@link ProvisionUtils#assembleRSAPublicKey(byte[])}. */ @Test - public void testAssemblePublicKeyUsingByteArray() { + public void testAssembleRSAPublicKeyUsingByteArray() { // obtain the expected modulus from the existing public key final BigInteger modulus = ((RSAPublicKey) keyPair.getPublic()).getModulus(); @@ -267,7 +308,7 @@ public void testAssemblePublicKeyUsingByteArray() { */ @Test public void testParseEk() throws URISyntaxException, IOException { - Path ekPath = Paths.get(Objects.requireNonNull(getClass().getResource(EK_PUBLIC_PATH)).toURI()); + Path ekPath = Paths.get(Objects.requireNonNull(getClass().getResource(EK_PUBLIC_KEY_PATH)).toURI()); byte[] ekFile = Files.readAllBytes(ekPath); @@ -295,7 +336,7 @@ public void testParseEk() throws URISyntaxException, IOException { */ @Test public void testParseAk() throws URISyntaxException, IOException { - Path akPath = Paths.get(Objects.requireNonNull(getClass().getResource(AK_PUBLIC_PATH)).toURI()); + Path akPath = Paths.get(Objects.requireNonNull(getClass().getResource(AK_PUBLIC_KEY_PATH)).toURI()); byte[] akFile = Files.readAllBytes(akPath); @@ -314,155 +355,4 @@ public void testParseAk() throws URISyntaxException, IOException { String realMod = AK_MODULUS_HEX.replaceAll("\\s+", ""); assertEquals(realMod, hex); } - - /** - * Tests {@link AttestationCertificateAuthorityService# - * 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(); - int validDays = 1; - - // create mocks for testing - IdentityProof identityProof = mock(IdentityProof.class); - AsymmetricPublicKey asymmetricPublicKey = mock(AsymmetricPublicKey.class); - StorePubKey storePubKey = mock(StorePubKey.class); - X509Certificate acaCertificate = createSelfSignedCertificate(keyPair); - - // assign ACA fields - ReflectionTestUtils.setField(attestationCertificateAuthorityService, "validDays", validDays); - ReflectionTestUtils.setField(attestationCertificateAuthorityService, "acaCertificate", acaCertificate); - - // prepare identity proof interactions - when(identityProof.getLabel()).thenReturn(identityProofLabel); - - // perform the test - X509Certificate certificate = abstractProcessor.accessGenerateCredential(keyPair.getPublic(), - null, - new LinkedList<>(), - "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 - verifyNoMoreInteractions(identityProof, asymmetricPublicKey, storePubKey); - } - - /** - * Creates a self-signed X.509 public-key certificate. - * - * @param pair KeyPair to create the cert for - * @return self-signed X509Certificate - */ - private X509Certificate createSelfSignedCertificate(final KeyPair pair) { - Security.addProvider(new BouncyCastleProvider()); - final int timeRange = 10000; - X509Certificate cert = null; - try { - X500Name issuerName = new X500Name("CN=TEST2, OU=TEST2, O=TEST2, C=TEST2"); - X500Name subjectName = new X500Name("CN=TEST, OU=TEST, O=TEST, C=TEST"); - BigInteger serialNumber = BigInteger.ONE; - Date notBefore = new Date(System.currentTimeMillis() - timeRange); - Date notAfter = new Date(System.currentTimeMillis() + timeRange); - X509v3CertificateBuilder builder = - new JcaX509v3CertificateBuilder(issuerName, serialNumber, notBefore, notAfter, - subjectName, pair.getPublic()); - ContentSigner signer = - new JcaContentSignerBuilder("SHA256WithRSA").setProvider("BC").build( - pair.getPrivate()); - return new JcaX509CertificateConverter().setProvider("BC").getCertificate( - builder.build(signer)); - } catch (Exception e) { - fail("Exception occurred while creating a cert", e); - } - return cert; - } - - - /** - * 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 platformCredentials, - final String deviceName, - final X509Certificate acaCertificate) { - - return generateCredential(publicKey, - endorsementCredential, - platformCredentials, - deviceName, - acaCertificate); - } - } - } diff --git a/HIRS_AttestationCA/src/test/resources/tpm2/ak.name b/HIRS_AttestationCA/src/test/resources/public_keys/ak.name similarity index 100% rename from HIRS_AttestationCA/src/test/resources/tpm2/ak.name rename to HIRS_AttestationCA/src/test/resources/public_keys/ak.name diff --git a/HIRS_AttestationCA/src/test/resources/tpm2/ak.pub b/HIRS_AttestationCA/src/test/resources/public_keys/ak.pub similarity index 100% rename from HIRS_AttestationCA/src/test/resources/tpm2/ak.pub rename to HIRS_AttestationCA/src/test/resources/public_keys/ak.pub diff --git a/HIRS_AttestationCA/src/test/resources/tpm2/ek.pub b/HIRS_AttestationCA/src/test/resources/public_keys/ek.pub similarity index 100% rename from HIRS_AttestationCA/src/test/resources/tpm2/ek.pub rename to HIRS_AttestationCA/src/test/resources/public_keys/ek.pub From ac90e40bb503c19b16d2a7fef1815809d5757511 Mon Sep 17 00:00:00 2001 From: ThatSilentCoder <184309164+ThatSilentCoder@users.noreply.github.com> Date: Thu, 26 Feb 2026 17:47:16 -0500 Subject: [PATCH 07/25] v3.1_issue_1090: Going to have to complete a function that makes credential using ecc. Fixed the enum class. Removed throws exception decorator from methods. Made abstract processor abstract. Might consider ditching the abstract processor and placing them in a utils class. This might be a bit more complicated than we imagined. --- ...ionCertificateAuthorityRestController.java | 4 +- .../persist/enums/PublicKeyAlgorithm.java | 15 +- .../persist/provision/AbstractProcessor.java | 6 +- .../provision/IdentityClaimProcessor.java | 14 +- .../provision/helper/ProvisionUtils.java | 214 +++++++++++------- ...tationCertificateAuthorityServiceTest.java | 2 +- 6 files changed, 143 insertions(+), 112 deletions(-) diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/AttestationCertificateAuthorityRestController.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/AttestationCertificateAuthorityRestController.java index 5e5f78f92..d7e7130d2 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/AttestationCertificateAuthorityRestController.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/AttestationCertificateAuthorityRestController.java @@ -10,8 +10,6 @@ import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; -import java.security.GeneralSecurityException; - /** * REST Controller for the ACA that communicates with the Provisioner during the provisioning process. */ @@ -41,7 +39,7 @@ public AttestationCertificateAuthorityRestController( */ @ResponseBody @PostMapping(value = "/identity-claim-tpm2/process", consumes = MediaType.APPLICATION_OCTET_STREAM_VALUE) - public byte[] processIdentityClaimTpm2(@RequestBody final byte[] identityClaim) throws GeneralSecurityException { + public byte[] processIdentityClaimTpm2(@RequestBody final byte[] identityClaim) { return this.attestationCertificateAuthorityService.processIdentityClaimTpm2(identityClaim); } diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/enums/PublicKeyAlgorithm.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/enums/PublicKeyAlgorithm.java index 06bf362be..8f1eeeb62 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/enums/PublicKeyAlgorithm.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/enums/PublicKeyAlgorithm.java @@ -4,10 +4,6 @@ import lombok.Getter; import lombok.ToString; -import java.security.PublicKey; -import java.security.interfaces.ECPublicKey; -import java.security.interfaces.RSAPublicKey; - /** * Enum representing public key algorithms for asymmetric cryptography. *

@@ -27,30 +23,25 @@ public enum PublicKeyAlgorithm { /** * RSA Public Key Algorithm. */ - RSA("RSA", RSAPublicKey.class), + RSA("RSA"), /** * ECC Public Key Algorithm. */ - ECC("ECC", ECPublicKey.class), + ECC("ECC"), /** * Represents an unknown public key algorithm. * This is used when the application encounters a public key algorithm that is not recognized or supported. * It may occur if the algorithm is unsupported or not implemented in the current version of the application. */ - UNKNOWN("UNKNOWN", null); + UNKNOWN("UNKNOWN"); /** * The name of the cryptographic algorithm. */ private final String algorithmName; - /** - * The Java Security class corresponding to the public key type used by the algorithm. - */ - private final Class keyClass; - /** * Converts the provided string public key algorithm into an ENUM. * 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 7d981da83..6b1ac7f3d 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 @@ -45,7 +45,7 @@ @Getter @Log4j2 @NoArgsConstructor -public class AbstractProcessor { +public abstract class AbstractProcessor { private int validDays; @@ -299,10 +299,10 @@ public boolean saveAttestationCertificate(final CertificateRepository certificat if (issuedAc != null && !issuedAc.isEmpty() && (ldevID ? policySettings.isGenerateDevIdCertificateOnExpiration() : policySettings.isGenerateAttestationCertificateOnExpiration())) { - if (issuedAc.get(0).getEndValidity().after(currentDate)) { + if (issuedAc.getFirst().getEndValidity().after(currentDate)) { // so the issued AC is not expired // however are we within the threshold - days = ProvisionUtils.daysBetween(currentDate, issuedAc.get(0).getEndValidity()); + days = ProvisionUtils.daysBetween(currentDate, issuedAc.getFirst().getEndValidity()); generateCertificate = days < (ldevID ? policySettings.getDevIdReissueThreshold() : policySettings.getReissueThreshold()); diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/IdentityClaimProcessor.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/IdentityClaimProcessor.java index 3dcaa6c46..fae13b497 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/IdentityClaimProcessor.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/IdentityClaimProcessor.java @@ -61,7 +61,6 @@ import java.security.NoSuchAlgorithmException; import java.security.PublicKey; import java.security.cert.CertificateException; -import java.security.interfaces.RSAPublicKey; import java.util.ArrayList; import java.util.Base64; import java.util.HashMap; @@ -164,11 +163,14 @@ public byte[] processIdentityClaimTpm2(final byte[] identityClaim) { // parse the EK Public key from the IdentityClaim once for use in supply chain validation // and later tpm20MakeCredential function - RSAPublicKey ekPub = ProvisionUtils.parseRSAKeyFromPublicDataSegment(claim.getEkPublicArea().toByteArray()); + PublicKey endorsementCredentialPublicKey = + ProvisionUtils.parsePublicKeyFromPublicDataSegment(publicKeyAlgorithm, + claim.getEkPublicArea().toByteArray()); + AppraisalStatus.Status validationResult = AppraisalStatus.Status.FAIL; try { - validationResult = doSupplyChainValidation(claim, ekPub); + validationResult = doSupplyChainValidation(claim, endorsementCredentialPublicKey); } catch (Exception ex) { log.error(ex.getMessage()); } @@ -176,9 +178,11 @@ public byte[] processIdentityClaimTpm2(final byte[] identityClaim) { ByteString blobStr = ByteString.copyFrom(new byte[]{}); if (validationResult == AppraisalStatus.Status.PASS) { - RSAPublicKey akPub = ProvisionUtils.parseRSAKeyFromPublicDataSegment(claim.getAkPublicArea().toByteArray()); + PublicKey akPub = ProvisionUtils.parsePublicKeyFromPublicDataSegment(publicKeyAlgorithm, + claim.getAkPublicArea().toByteArray()); byte[] nonce = ProvisionUtils.generateRandomBytes(NONCE_LENGTH); - blobStr = ProvisionUtils.tpm20MakeCredential(ekPub, akPub, nonce); + blobStr = ProvisionUtils.tpm20MakeCredential(publicKeyAlgorithm, endorsementCredentialPublicKey, akPub, + nonce); String pcrQuoteMask = PCR_QUOTE_MASK; diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/helper/ProvisionUtils.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/helper/ProvisionUtils.java index 7ecda93ac..d80e0c013 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/helper/ProvisionUtils.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/helper/ProvisionUtils.java @@ -36,10 +36,10 @@ import java.security.cert.X509Certificate; import java.security.interfaces.ECPublicKey; import java.security.interfaces.RSAPublicKey; -import java.security.spec.ECPoint; import java.security.spec.InvalidKeySpecException; import java.security.spec.MGF1ParameterSpec; import java.security.spec.RSAPublicKeySpec; +import java.security.spec.X509EncodedKeySpec; import java.util.Date; @Log4j2 @@ -85,7 +85,7 @@ public final class ProvisionUtils { private static final int DEFAULT_RSA_MODULUS_LENGTH_IN_BYTES = 256; - private static final int DEFAULT_ECC_CURVE_LENGTH_IN_BYTES = 32; + private static final int DEFAULT_ECC_KEY_LENGTH_IN_BYTES = 32; // Constants used to parse out the ak name from the ak public data. Used in generateAkName private static final String AK_NAME_PREFIX = "000b"; @@ -156,18 +156,18 @@ public static String getPemEncodedCertificate(final X509Certificate certificate) } /** - * Parses a public key from a byte array and returns it as a generic PublicKey. - * Currently supports RSA and ECC. + * Parses a public key from a byte array using one of the two supported public key + * algorithm and returns it as a generic PublicKey. * * @param publicKeyAlgorithm public key algorithm - * @param keyBytes the DER-encoded public key bytes + * @param publicAreaSegment public area segment generated by TPM 2.0 * @return the parsed PublicKey instance */ public static PublicKey parsePublicKeyFromPublicDataSegment(final PublicKeyAlgorithm publicKeyAlgorithm, - final byte[] keyBytes) { + final byte[] publicAreaSegment) { return switch (publicKeyAlgorithm) { - case RSA -> parseRSAKeyFromPublicDataSegment(keyBytes); - case ECC -> parseECCKeyFromPublicDataSegment(keyBytes); + case RSA -> parseRSAKeyFromPublicDataSegment(publicAreaSegment); + case ECC -> parseECCKeyFromPublicDataSegment(publicAreaSegment); default -> throw new UnsupportedOperationException("Unsupported or invalid public key algorithm"); }; } @@ -191,7 +191,7 @@ public static RSAPublicKey parseRSAKeyFromPublicDataSegment(final byte[] publicA byte[] modulus = HexUtils.subarray(publicAreaSegment, publicAreaSegmentLen - DEFAULT_RSA_MODULUS_LENGTH_IN_BYTES, publicAreaSegmentLen - 1); - return (RSAPublicKey) assembleRSAPublicKey(modulus); + return assembleRSAPublicKey(modulus); } /** @@ -200,7 +200,7 @@ public static RSAPublicKey parseRSAKeyFromPublicDataSegment(final byte[] publicA * @param modulus in byte array form * @return RSA public key using specific modulus and the well known exponent */ - public static PublicKey assembleRSAPublicKey(final byte[] modulus) { + public static RSAPublicKey assembleRSAPublicKey(final byte[] modulus) { final String modulusHexString = Hex.encodeHexString(modulus); return assembleRSAPublicKey(new BigInteger(modulusHexString, DEFAULT_IV_SIZE)); } @@ -211,14 +211,14 @@ public static PublicKey assembleRSAPublicKey(final byte[] modulus) { * @param modulus modulus * @return RSA public key using the provided integer modulus */ - public static PublicKey assembleRSAPublicKey(final BigInteger modulus) { + public static RSAPublicKey assembleRSAPublicKey(final BigInteger modulus) { // generate an RSA key spec using mod and exp RSAPublicKeySpec keySpec = new RSAPublicKeySpec(modulus, EXPONENT); // create the RSA public key try { KeyFactory keyFactory = KeyFactory.getInstance("RSA"); - return keyFactory.generatePublic(keySpec); + return (RSAPublicKey) keyFactory.generatePublic(keySpec); } catch (NoSuchAlgorithmException | InvalidKeySpecException e) { throw new UnexpectedServerException( "Encountered unexpected error creating RSA public key: " + e.getMessage(), e); @@ -235,112 +235,106 @@ public static PublicKey assembleRSAPublicKey(final BigInteger modulus) { public static ECPublicKey parseECCKeyFromPublicDataSegment(final byte[] publicAreaSegment) { final int publicAreaSegmentLen = publicAreaSegment.length; - if (publicAreaSegmentLen > DEFAULT_ECC_CURVE_LENGTH_IN_BYTES) { + if (publicAreaSegmentLen < DEFAULT_ECC_KEY_LENGTH_IN_BYTES) { final String errorMsg = "Could not parse ECC Public Key due to public data segment not being long enough."; log.error(errorMsg); throw new IllegalArgumentException(errorMsg); } -// final BigInteger x = new BigInteger("0"); -// final BigInteger y = new BigInteger("0"); + byte[] eccKeySection = + HexUtils.subarray(publicAreaSegment, publicAreaSegmentLen - DEFAULT_ECC_KEY_LENGTH_IN_BYTES, + publicAreaSegmentLen - 1); - return new ECPublicKey() { - @Override - public ECPoint getW() { - return null; - } + return assembleECCPublicKey(eccKeySection); + } - @Override - public String getAlgorithm() { - return ""; - } + /** + * todo + */ + public static ECPublicKey assembleECCPublicKey(final byte[] eccKeySection) { + // create the ECC public key + try { + KeyFactory keyFactory = KeyFactory.getInstance("EC"); - @Override - public String getFormat() { - return ""; - } + // Create an X509EncodedKeySpec object with the byte array + X509EncodedKeySpec spec = new X509EncodedKeySpec(eccKeySection); - @Override - public byte[] getEncoded() { - return new byte[0]; - } - }; + // Generate the ECC public key from the byte array + return (ECPublicKey) keyFactory.generatePublic(spec); + } catch (NoSuchAlgorithmException | InvalidKeySpecException e) { + throw new UnexpectedServerException( + "Encountered unexpected error creating ECC public key: " + e.getMessage(), e); + } } -// /** -// * todo -// * -// * @param ecPoint -// * @return -// */ -// public static ECPublicKey assembleECCPublicKey(final ECPoint ecPoint) { -// BigInteger a = new BigInteger("0"); -// BigInteger b = new BigInteger("0"); -// -// EllipticCurve ellipticCurve = new EllipticCurve(null, a, b); -// ECParameterSpec ecParameterSpec = null; -// -// return (ECPublicKey) assembleECCPublicKey(ecPoint, ecParameterSpec); -// } -// -// /** -// * todo -// * -// * @param ecpoint -// * @param ecParameterSpec -// * @return -// */ -// public static PublicKey assembleECCPublicKey(final ECPoint ecpoint, final ECParameterSpec ecParameterSpec) { -// final ECPublicKeySpec ecPublicKeySpec = new ECPublicKeySpec(ecpoint, ecParameterSpec); -// -// // create the ECC public key -// try { -// KeyFactory keyFactory = KeyFactory.getInstance(PublicKeyAlgorithm.ECC.getAlgorithmName()); -// return keyFactory.generatePublic(ecPublicKeySpec); -// } catch (NoSuchAlgorithmException | InvalidKeySpecException e) { -// throw new UnexpectedServerException( -// "Encountered unexpected error creating ECC public key: " + e.getMessage(), e); -// } -// } - /** - * Performs the first step of the TPM 2.0 identity claim process. Takes an ek, ak, and secret - * and then generates a seed that is used to generate AES and HMAC keys. Parses the ak name. - * Encrypts the seed with the public ek. Uses the AES key to encrypt the secret. Uses the HMAC - * key to generate an HMAC to cover the encrypted secret and the ak name. The output is an - * encrypted blob that acts as the first part of a challenge-response authentication mechanism - * to validate an identity claim. - *

- * Equivalent to calling tpm2_makecredential using tpm2_tools. - * - * @param ek endorsement key in the identity claim - * @param ak attestation key in the identity claim - * @param secret a nonce + * @param publicKeyAlgorithm public key algorithm + * @param endorsementPublicKey endorsement key in the identity claim + * @param attestationPublicKey attestation key in the identity claim + * @param secret a nonce * @return the encrypted blob forming the identity claim challenge */ - public static ByteString tpm20MakeCredential(final RSAPublicKey ek, final RSAPublicKey ak, - final byte[] secret) { + public static ByteString tpm20MakeCredential( + final PublicKeyAlgorithm publicKeyAlgorithm, + final PublicKey endorsementPublicKey, + final PublicKey attestationPublicKey, + final byte[] secret) { // check size of the secret if (secret.length > MAX_SECRET_LENGTH) { throw new IllegalArgumentException("Secret must be " + MAX_SECRET_LENGTH + " bytes or smaller."); } + return switch (publicKeyAlgorithm) { + case RSA -> tpm20MakeCredentialUsingRSA( + (RSAPublicKey) endorsementPublicKey, + (RSAPublicKey) attestationPublicKey, secret); + case ECC -> tpm20MakeCredentialUsingECC( + (ECPublicKey) endorsementPublicKey, + (ECPublicKey) attestationPublicKey, secret); + default -> throw new UnsupportedOperationException("Unsupported or invalid public key algorithm"); + }; + } + + /** + * Performs the first step of the TPM 2.0 identity claim process using the RSA public key algorithm: + *

+ * + *

+ * The output is an encrypted blob that acts as the first part of a challenge-response authentication mechanism + * to validate an identity claim. + *

+ * Equivalent to calling tpm2_makecredential using tpm2_tools. + * + * @param endorsementRSAKey endorsement key in the identity claim + * @param attestationRSAKey attestation key in the identity claim + * @param secret a nonce + * @return the encrypted blob forming the identity claim challenge + */ + public static ByteString tpm20MakeCredentialUsingRSA(final RSAPublicKey endorsementRSAKey, + final RSAPublicKey attestationRSAKey, + final byte[] secret) { // generate a random 32 byte seed byte[] seed = generateRandomBytes(SEED_LENGTH); try { - // encrypt seed with pubEk + // encrypt seed with endorsement RSA Public Key Cipher asymCipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding"); OAEPParameterSpec oaepSpec = new OAEPParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA256, new PSource.PSpecified("IDENTITY\0".getBytes(StandardCharsets.UTF_8))); - asymCipher.init(Cipher.PUBLIC_KEY, ek, oaepSpec); + asymCipher.init(Cipher.PUBLIC_KEY, endorsementRSAKey, oaepSpec); asymCipher.update(seed); byte[] encSeed = asymCipher.doFinal(); // generate ak name from akMod - byte[] akModTemp = ak.getModulus().toByteArray(); + byte[] akModTemp = attestationRSAKey.getModulus().toByteArray(); byte[] akMod = new byte[DEFAULT_RSA_MODULUS_LENGTH_IN_BYTES]; int startpos = 0; // BigIntegers are signed, so a modulus that has a first bit of 1 @@ -392,11 +386,54 @@ public static ByteString tpm20MakeCredential(final RSAPublicKey ek, final RSAPub | InvalidKeyException | InvalidAlgorithmParameterException | NoSuchPaddingException e) { throw new IdentityProcessingException( - "Encountered error while making the identity claim challenge: " + "Encountered error while making the identity claim challenge for the provided RSA public keys: " + e.getMessage(), e); } } + /** + * Performs the first step of the TPM 2.0 identity claim process using the ECC public key algorithm: + *

+ * + *

+ * The output is an encrypted blob that acts as the first part of a challenge-response authentication mechanism + * to validate an identity claim. + *

+ * Equivalent to calling tpm2_makecredential using tpm2_tools. + * + * @param endorsementECCKey endorsement key in the identity claim + * @param attestationECCKey attestation key in the identity claim + * @param secret a nonce + * @return the encrypted blob forming the identity claim challenge + */ + public static ByteString tpm20MakeCredentialUsingECC(final ECPublicKey endorsementECCKey, + final ECPublicKey attestationECCKey, + final byte[] secret) { + + // generate a random 32 byte seed + byte[] seed = generateRandomBytes(SEED_LENGTH); + + try { + // encrypt seed with endorsement ECC Public Key + Cipher asymCipher = Cipher.getInstance(""); + + } catch (Exception e) { + throw new IdentityProcessingException( + "Encountered error while making the identity claim challenge for the " + + "provided ECC public keys: " + + e.getMessage(), e); + } + + //todo + return ByteString.copyFrom(new byte[]{}); + } + /** * Assembles a credential blob. * @@ -406,7 +443,8 @@ public static ByteString tpm20MakeCredential(final RSAPublicKey ek, final RSAPub * @param encryptedSeed byte array representation of the encrypted seed * @return byte array representation of a credential blob */ - public static byte[] assembleCredential(final byte[] topSize, final byte[] integrityHmac, + public static byte[] assembleCredential(final byte[] topSize, + final byte[] integrityHmac, final byte[] encryptedSecret, final byte[] encryptedSeed) { /* diff --git a/HIRS_AttestationCA/src/test/java/hirs/attestationca/persist/AttestationCertificateAuthorityServiceTest.java b/HIRS_AttestationCA/src/test/java/hirs/attestationca/persist/AttestationCertificateAuthorityServiceTest.java index 8862c3797..0ceca17a4 100644 --- a/HIRS_AttestationCA/src/test/java/hirs/attestationca/persist/AttestationCertificateAuthorityServiceTest.java +++ b/HIRS_AttestationCA/src/test/java/hirs/attestationca/persist/AttestationCertificateAuthorityServiceTest.java @@ -284,7 +284,7 @@ public void testAssembleRSAPublicKeyUsingByteArray() { final BigInteger modulus = ((RSAPublicKey) keyPair.getPublic()).getModulus(); // perform test - RSAPublicKey publicKey = (RSAPublicKey) ProvisionUtils.assembleRSAPublicKey(modulus.toByteArray()); + RSAPublicKey publicKey = ProvisionUtils.assembleRSAPublicKey(modulus.toByteArray()); // assert that the exponent and the modulus are the same. the exponents should be the well // known prime, 101 From b3b02595b9e35e0049a5133ea3fc4c344c28f70a Mon Sep 17 00:00:00 2001 From: ThatSilentCoder <184309164+ThatSilentCoder@users.noreply.github.com> Date: Thu, 26 Feb 2026 17:52:39 -0500 Subject: [PATCH 08/25] v3.1_issue_1090: Just to quiet spotbug error. --- .../persist/provision/helper/ProvisionUtils.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/helper/ProvisionUtils.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/helper/ProvisionUtils.java index d80e0c013..c6bc14e25 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/helper/ProvisionUtils.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/helper/ProvisionUtils.java @@ -417,11 +417,11 @@ public static ByteString tpm20MakeCredentialUsingECC(final ECPublicKey endorseme final byte[] secret) { // generate a random 32 byte seed - byte[] seed = generateRandomBytes(SEED_LENGTH); + //byte[] seed = generateRandomBytes(SEED_LENGTH); try { // encrypt seed with endorsement ECC Public Key - Cipher asymCipher = Cipher.getInstance(""); + // Cipher asymCipher = Cipher.getInstance(""); } catch (Exception e) { throw new IdentityProcessingException( From ad14f4897d5764b3c4c8a2a08f1176069418ac68 Mon Sep 17 00:00:00 2001 From: ThatSilentCoder <184309164+ThatSilentCoder@users.noreply.github.com> Date: Fri, 27 Feb 2026 14:45:01 -0500 Subject: [PATCH 09/25] v3.1_issue_1090: Changed variable names in the provisionutils parser methods, moved the restconrtoller, service and service impl to a package that makes sense for these classes, deleted abstract processor, moved methods that pertain to each procressor to their respective processor and moved any common utilities to a util function. Might need to change the way we extract the public key algorithm for this to work properly. --- .../persist/provision/AbstractProcessor.java | 347 ------------------ ...ionCertificateAuthorityRestController.java | 2 +- ...ttestationCertificateAuthorityService.java | 2 +- ...tationCertificateAuthorityServiceImpl.java | 4 +- .../CertificateRequestProcessor.java | 181 ++++++++- .../provision/IdentityClaimProcessor.java | 11 +- .../helper/CredentialManagementHelper.java | 143 ++++++++ .../provision/helper/ProvisionUtils.java | 24 +- ...tationCertificateAuthorityServiceTest.java | 2 + package/linux/aca/aca_bootRun.sh | 8 +- package/linux/aca/aca_setup.sh | 4 +- 11 files changed, 347 insertions(+), 381 deletions(-) delete mode 100644 HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/AbstractProcessor.java rename HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/{ => provision}/AttestationCertificateAuthorityRestController.java (98%) rename HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/{ => provision}/AttestationCertificateAuthorityService.java (95%) rename HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/{ => provision}/AttestationCertificateAuthorityServiceImpl.java (92%) 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 deleted file mode 100644 index 6b1ac7f3d..000000000 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/AbstractProcessor.java +++ /dev/null @@ -1,347 +0,0 @@ -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.PolicyRepository; -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.certificate.EndorsementCredential; -import hirs.attestationca.persist.entity.userdefined.certificate.IssuedAttestationCertificate; -import hirs.attestationca.persist.entity.userdefined.certificate.PlatformCredential; -import hirs.attestationca.persist.exceptions.CertificateProcessingException; -import hirs.attestationca.persist.provision.helper.CredentialManagementHelper; -import hirs.attestationca.persist.provision.helper.IssuedCertificateAttributeHelper; -import hirs.attestationca.persist.provision.helper.ProvisionUtils; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; -import lombok.extern.log4j.Log4j2; -import org.bouncycastle.asn1.x500.X500Name; -import org.bouncycastle.asn1.x509.BasicConstraints; -import org.bouncycastle.asn1.x509.Extension; -import org.bouncycastle.asn1.x509.KeyUsage; -import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; -import org.bouncycastle.cert.X509CertificateHolder; -import org.bouncycastle.cert.X509v3CertificateBuilder; -import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; -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; -import java.security.PrivateKey; -import java.security.PublicKey; -import java.security.cert.CertificateException; -import java.security.cert.X509Certificate; -import java.util.Calendar; -import java.util.Date; -import java.util.LinkedList; -import java.util.List; - -@Getter -@Log4j2 -@NoArgsConstructor -public abstract class AbstractProcessor { - - private int validDays; - - private PrivateKey privateKey; - - @Setter - 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; - this.validDays = validDays; - } - - /** - * Generates a credential using the specified public key. - * - * @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 object used to create credential - * @return identity credential - */ - protected X509Certificate generateCredential(final PublicKey publicKey, - final EndorsementCredential endorsementCredential, - final List platformCredentials, - final String deviceName, - final X509Certificate acaCertificate) { - try { - // have the certificate expire in the configured number of days - Calendar expiry = Calendar.getInstance(); - expiry.add(Calendar.DAY_OF_YEAR, getValidDays()); - - X500Name issuer = - new X509CertificateHolder(acaCertificate.getEncoded()).getSubject(); - Date notBefore = new Date(); - Date notAfter = expiry.getTime(); - BigInteger serialNumber = BigInteger.valueOf(System.currentTimeMillis()); - - SubjectPublicKeyInfo subjectPublicKeyInfo = - SubjectPublicKeyInfo.getInstance(publicKey.getEncoded()); - - // The subject should be left blank, per spec - X509v3CertificateBuilder builder = - new X509v3CertificateBuilder(issuer, serialNumber, - notBefore, notAfter, null /* subjectName */, subjectPublicKeyInfo); - - Extension subjectAlternativeName = - IssuedCertificateAttributeHelper.buildSubjectAlternativeNameFromCerts( - endorsementCredential, platformCredentials, deviceName); - - Extension authKeyIdentifier = IssuedCertificateAttributeHelper - .buildAuthorityKeyIdentifier(acaCertificate); - - builder.addExtension(subjectAlternativeName); - if (authKeyIdentifier != null) { - builder.addExtension(authKeyIdentifier); - } - // identify cert as an AIK with this extension - if (IssuedCertificateAttributeHelper.EXTENDED_KEY_USAGE_EXTENSION != null) { - builder.addExtension(IssuedCertificateAttributeHelper.EXTENDED_KEY_USAGE_EXTENSION); - } else { - log.warn("Failed to build extended key usage extension and add to AIK"); - throw new IllegalStateException("Extended Key Usage attribute unavailable. " - + "Unable to issue certificates"); - } - - // Add signing extension - builder.addExtension( - Extension.keyUsage, - true, - new KeyUsage(KeyUsage.digitalSignature | KeyUsage.keyEncipherment) - ); - - // Basic constraints - builder.addExtension( - Extension.basicConstraints, - true, - new BasicConstraints(false) - ); - - ContentSigner signer = new JcaContentSignerBuilder("SHA256WithRSA") - .setProvider("BC").build(getPrivateKey()); - X509CertificateHolder holder = builder.build(signer); - return new JcaX509CertificateConverter() - .setProvider("BC").getCertificate(holder); - } catch (IOException | OperatorCreationException | CertificateException exception) { - throw new CertificateProcessingException("Encountered error while generating " - + "identity credential: " + exception.getMessage(), exception); - } - } - - /** - * Helper method to parse an Endorsement Credential from a Protobuf generated - * IdentityClaim. Will also check if the Endorsement Credential was already uploaded. - * Persists the Endorsement Credential if it does not already exist. - * - * @param identityClaim a Protobuf generated Identity Claim object - * @param ekPub the endorsement public key from the Identity Claim object - * @param certificateRepository db connector from certificates - * @return the Endorsement Credential, if one exists, null otherwise - */ - protected EndorsementCredential parseEcFromIdentityClaim( - final ProvisionerTpm2.IdentityClaim identityClaim, - final PublicKey ekPub, final CertificateRepository certificateRepository) { - EndorsementCredential endorsementCredential = null; - - if (identityClaim.hasEndorsementCredential()) { - endorsementCredential = CredentialManagementHelper.storeEndorsementCredential( - certificateRepository, - identityClaim.getEndorsementCredential().toByteArray(), - identityClaim.getDv().getNw().getHostname()); - } else if (ekPub != null) { - log.warn("Endorsement Cred was not in the identity claim from the client." - + " Checking for uploads."); - endorsementCredential = getEndorsementCredential(ekPub, certificateRepository); - } else { - log.warn("No endorsement credential was received in identity claim and no EK Public" - + " Key was provided to check for uploaded certificates."); - } - - return endorsementCredential; - } - - /** - * Helper method to parse a set of Platform Credentials from a Protobuf generated - * IdentityClaim and Endorsement Credential. Persists the Platform Credentials if they - * do not already exist. - * - * @param identityClaim a Protobuf generated Identity Claim object - * @param endorsementCredential an endorsement credential to check if platform credentials - * exist - * @param certificateRepository db connector from certificates - * @return the List of Platform Credentials, if they exist, an empty set otherwise - */ - protected List parsePcsFromIdentityClaim( - final ProvisionerTpm2.IdentityClaim identityClaim, - final EndorsementCredential endorsementCredential, - final CertificateRepository certificateRepository) { - List platformCredentials = new LinkedList<>(); - - if (identityClaim.getPlatformCredentialCount() > 0) { - - List platformCredentialList = identityClaim.getPlatformCredentialList(); - - for (ByteString platformCredential : platformCredentialList) { - if (!platformCredential.isEmpty()) { - PlatformCredential storedPlatformCredential = - CredentialManagementHelper.storePlatformCredential( - certificateRepository, platformCredential.toByteArray(), - identityClaim.getDv().getNw().getHostname()); - - if (storedPlatformCredential != null) { - platformCredentials.add(storedPlatformCredential); - } - } - } - } else if (endorsementCredential != null) { - // if none in the identity claim, look for uploaded platform credentials - log.warn("PC was not in the identity claim from the client. Checking for uploads."); - platformCredentials.addAll(getPlatformCredentials(certificateRepository, endorsementCredential)); - } else { - log.warn("No platform credential received in identity claim."); - } - - return platformCredentials; - } - - /** - * 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) { - log.debug("Searching for endorsement credential based on public key: {}", ekPublicKey); - - if (ekPublicKey == null) { - throw new IllegalArgumentException("Cannot look up an EC given a null public key"); - } - - EndorsementCredential credential = null; - - try { - credential = certificateRepository.findByPublicKeyModulusHexValue( - Certificate.getPublicKeyModulus(ekPublicKey) - .toString(Certificate.HEX_BASE)); - } catch (IOException e) { - log.error("Could not extract public key modulus", e); - } - - if (credential == null) { - log.warn("Unable to find endorsement credential for public key."); - } else { - log.debug("Endorsement credential found."); - } - - return credential; - } - - /** - * 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 - * @param platformCredentials the platform credentials used to generate the AC - * @param device the device to which the attestation certificate is tied - * @param ldevID whether the certificate is a ldevid - * @return whether the certificate was saved successfully - */ - public boolean saveAttestationCertificate(final CertificateRepository certificateRepository, - final byte[] derEncodedAttestationCertificate, - final EndorsementCredential endorsementCredential, - final List platformCredentials, - final Device device, - final boolean ldevID) { - List issuedAc; - boolean generateCertificate = true; - PolicyRepository scp = getPolicyRepository(); - PolicySettings policySettings; - Date currentDate = new Date(); - int days; - try { - // save issued certificate - IssuedAttestationCertificate attCert = new IssuedAttestationCertificate( - derEncodedAttestationCertificate, endorsementCredential, platformCredentials, ldevID); - - if (scp != null) { - policySettings = scp.findByName("Default"); - - Sort sortCriteria = Sort.by(Sort.Direction.DESC, "endValidity"); - issuedAc = certificateRepository.findByDeviceIdAndLdevID(device.getId(), ldevID, - sortCriteria); - - generateCertificate = ldevID ? policySettings.isIssueDevIdCertificateEnabled() - : policySettings.isIssueAttestationCertificateEnabled(); - - if (issuedAc != null && !issuedAc.isEmpty() - && (ldevID ? policySettings.isGenerateDevIdCertificateOnExpiration() - : policySettings.isGenerateAttestationCertificateOnExpiration())) { - if (issuedAc.getFirst().getEndValidity().after(currentDate)) { - // so the issued AC is not expired - // however are we within the threshold - days = ProvisionUtils.daysBetween(currentDate, issuedAc.getFirst().getEndValidity()); - generateCertificate = - days < (ldevID ? policySettings.getDevIdReissueThreshold() - : policySettings.getReissueThreshold()); - } - } - } - - if (generateCertificate) { - attCert.setDeviceId(device.getId()); - attCert.setDeviceName(device.getName()); - certificateRepository.save(attCert); - } - } catch (Exception e) { - log.error("Error saving generated Attestation Certificate to database.", e); - throw new CertificateProcessingException( - "Encountered error while storing Attestation Certificate: " - + e.getMessage(), e); - } - - return generateCertificate; - } - - private List getPlatformCredentials(final CertificateRepository certificateRepository, - final EndorsementCredential ec) { - List 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()); - } - } - - return credentials; - } -} diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/AttestationCertificateAuthorityRestController.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/AttestationCertificateAuthorityRestController.java similarity index 98% rename from HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/AttestationCertificateAuthorityRestController.java rename to HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/AttestationCertificateAuthorityRestController.java index d7e7130d2..d61680f78 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/AttestationCertificateAuthorityRestController.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/AttestationCertificateAuthorityRestController.java @@ -1,4 +1,4 @@ -package hirs.attestationca.persist; +package hirs.attestationca.persist.provision; import lombok.extern.log4j.Log4j2; import org.springframework.beans.factory.annotation.Autowired; diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/AttestationCertificateAuthorityService.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/AttestationCertificateAuthorityService.java similarity index 95% rename from HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/AttestationCertificateAuthorityService.java rename to HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/AttestationCertificateAuthorityService.java index cdb9f7081..e1658f29c 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/AttestationCertificateAuthorityService.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/AttestationCertificateAuthorityService.java @@ -1,4 +1,4 @@ -package hirs.attestationca.persist; +package hirs.attestationca.persist.provision; /** * Interface that defines the responsibilities of the Attestation Certificate Authority service. diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/AttestationCertificateAuthorityServiceImpl.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/AttestationCertificateAuthorityServiceImpl.java similarity index 92% rename from HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/AttestationCertificateAuthorityServiceImpl.java rename to HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/AttestationCertificateAuthorityServiceImpl.java index 3f89ff106..7b404e7ae 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/AttestationCertificateAuthorityServiceImpl.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/AttestationCertificateAuthorityServiceImpl.java @@ -1,7 +1,5 @@ -package hirs.attestationca.persist; +package hirs.attestationca.persist.provision; -import hirs.attestationca.persist.provision.CertificateRequestProcessor; -import hirs.attestationca.persist.provision.IdentityClaimProcessor; import lombok.extern.log4j.Log4j2; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; 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 46898e34e..a6bcc0d79 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 @@ -12,36 +12,59 @@ import hirs.attestationca.persist.entity.userdefined.PolicySettings; import hirs.attestationca.persist.entity.userdefined.SupplyChainValidationSummary; 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.TPMInfo; import hirs.attestationca.persist.entity.userdefined.report.DeviceInfoReport; import hirs.attestationca.persist.enums.AppraisalStatus; import hirs.attestationca.persist.enums.PublicKeyAlgorithm; import hirs.attestationca.persist.exceptions.CertificateProcessingException; +import hirs.attestationca.persist.provision.helper.CredentialManagementHelper; +import hirs.attestationca.persist.provision.helper.IssuedCertificateAttributeHelper; import hirs.attestationca.persist.provision.helper.ProvisionUtils; import hirs.attestationca.persist.service.SupplyChainValidationService; import lombok.extern.log4j.Log4j2; import org.apache.commons.lang3.ArrayUtils; +import org.bouncycastle.asn1.x500.X500Name; +import org.bouncycastle.asn1.x509.BasicConstraints; +import org.bouncycastle.asn1.x509.Extension; +import org.bouncycastle.asn1.x509.KeyUsage; +import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; +import org.bouncycastle.cert.X509CertificateHolder; +import org.bouncycastle.cert.X509v3CertificateBuilder; +import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; +import org.bouncycastle.operator.ContentSigner; +import org.bouncycastle.operator.OperatorCreationException; +import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; +import org.springframework.data.domain.Sort; import org.springframework.stereotype.Service; +import java.io.IOException; +import java.math.BigInteger; import java.nio.charset.StandardCharsets; import java.security.PrivateKey; import java.security.PublicKey; +import java.security.cert.CertificateException; import java.security.cert.X509Certificate; +import java.util.Calendar; +import java.util.Date; import java.util.List; @Service @Log4j2 -public class CertificateRequestProcessor extends AbstractProcessor { +public class CertificateRequestProcessor { private final SupplyChainValidationService supplyChainValidationService; private final CertificateRepository certificateRepository; private final DeviceRepository deviceRepository; private final X509Certificate acaCertificate; private final TPM2ProvisionerStateRepository tpm2ProvisionerStateRepository; + private final PolicyRepository policyRepository; private final PublicKeyAlgorithm publicKeyAlgorithm; + private final int certificateValidityInDays; + private final PrivateKey privateKey; /** * Constructor. @@ -65,14 +88,15 @@ public CertificateRequestProcessor(final SupplyChainValidationService supplyChai @Value("${aca.certificates.validity}") final int certificateValidityInDays, final TPM2ProvisionerStateRepository tpm2ProvisionerStateRepository, final PolicyRepository policyRepository) { - super(privateKey, certificateValidityInDays); + this.certificateValidityInDays = certificateValidityInDays; this.supplyChainValidationService = supplyChainValidationService; this.certificateRepository = certificateRepository; this.deviceRepository = deviceRepository; this.acaCertificate = acaCertificate; this.tpm2ProvisionerStateRepository = tpm2ProvisionerStateRepository; this.publicKeyAlgorithm = PublicKeyAlgorithm.fromString(publicKeyAlgorithmStr); - setPolicyRepository(policyRepository); + this.policyRepository = policyRepository; + this.privateKey = privateKey; } /** @@ -102,7 +126,6 @@ public byte[] processCertificateRequest(final byte[] certificateRequest) { throw new IllegalArgumentException(errorMsg); } - final PolicyRepository policyRepository = this.getPolicyRepository(); final PolicySettings policySettings = policyRepository.findByName("Default"); // attempt to deserialize Protobuf CertificateRequest @@ -139,10 +162,11 @@ public byte[] processCertificateRequest(final byte[] certificateRequest) { claim.getAkPublicArea().toByteArray()); // Get Endorsement Credential if it exists or was uploaded - EndorsementCredential endorsementCredential = parseEcFromIdentityClaim(claim, ekPub, certificateRepository); + EndorsementCredential endorsementCredential = + CredentialManagementHelper.parseEcFromIdentityClaim(claim, ekPub, certificateRepository); // Get Platform Credentials if they exist or were uploaded - List platformCredentials = parsePcsFromIdentityClaim(claim, + List platformCredentials = CredentialManagementHelper.parsePcsFromIdentityClaim(claim, endorsementCredential, certificateRepository); // Get LDevID public key if it exists @@ -409,4 +433,149 @@ private AppraisalStatus.Status doQuoteValidation(final Device device) { return validationResult; } + + /** + * Generates a credential using the specified public key. + * + * @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 object used to create credential + * @return identity credential + */ + private X509Certificate generateCredential(final PublicKey publicKey, + final EndorsementCredential endorsementCredential, + final List platformCredentials, + final String deviceName, + final X509Certificate acaCertificate) { + try { + // have the certificate expire in the configured number of days + Calendar expiry = Calendar.getInstance(); + expiry.add(Calendar.DAY_OF_YEAR, certificateValidityInDays); + + X500Name issuer = + new X509CertificateHolder(acaCertificate.getEncoded()).getSubject(); + Date notBefore = new Date(); + Date notAfter = expiry.getTime(); + BigInteger serialNumber = BigInteger.valueOf(System.currentTimeMillis()); + + SubjectPublicKeyInfo subjectPublicKeyInfo = + SubjectPublicKeyInfo.getInstance(publicKey.getEncoded()); + + // The subject should be left blank, per spec + X509v3CertificateBuilder builder = + new X509v3CertificateBuilder(issuer, serialNumber, + notBefore, notAfter, null /* subjectName */, subjectPublicKeyInfo); + + Extension subjectAlternativeName = + IssuedCertificateAttributeHelper.buildSubjectAlternativeNameFromCerts( + endorsementCredential, platformCredentials, deviceName); + + Extension authKeyIdentifier = IssuedCertificateAttributeHelper + .buildAuthorityKeyIdentifier(acaCertificate); + + builder.addExtension(subjectAlternativeName); + if (authKeyIdentifier != null) { + builder.addExtension(authKeyIdentifier); + } + // identify cert as an AIK with this extension + if (IssuedCertificateAttributeHelper.EXTENDED_KEY_USAGE_EXTENSION != null) { + builder.addExtension(IssuedCertificateAttributeHelper.EXTENDED_KEY_USAGE_EXTENSION); + } else { + log.warn("Failed to build extended key usage extension and add to AIK"); + throw new IllegalStateException("Extended Key Usage attribute unavailable. " + + "Unable to issue certificates"); + } + + // Add signing extension + builder.addExtension( + Extension.keyUsage, + true, + new KeyUsage(KeyUsage.digitalSignature | KeyUsage.keyEncipherment) + ); + + // Basic constraints + builder.addExtension( + Extension.basicConstraints, + true, + new BasicConstraints(false) + ); + + ContentSigner signer = new JcaContentSignerBuilder("SHA256WithRSA") + .setProvider("BC").build(privateKey); + X509CertificateHolder holder = builder.build(signer); + return new JcaX509CertificateConverter() + .setProvider("BC").getCertificate(holder); + } catch (IOException | OperatorCreationException | CertificateException exception) { + throw new CertificateProcessingException("Encountered error while generating " + + "identity credential: " + exception.getMessage(), exception); + } + } + + /** + * 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 + * @param platformCredentials the platform credentials used to generate the AC + * @param device the device to which the attestation certificate is tied + * @param ldevID whether the certificate is a ldevid + * @return whether the certificate was saved successfully + */ + public boolean saveAttestationCertificate(final CertificateRepository certificateRepository, + final byte[] derEncodedAttestationCertificate, + final EndorsementCredential endorsementCredential, + final List platformCredentials, + final Device device, + final boolean ldevID) { + List issuedAc; + boolean generateCertificate = true; + PolicySettings policySettings; + Date currentDate = new Date(); + int days; + try { + // save issued certificate + IssuedAttestationCertificate attCert = new IssuedAttestationCertificate( + derEncodedAttestationCertificate, endorsementCredential, platformCredentials, ldevID); + + policySettings = policyRepository.findByName("Default"); + + Sort sortCriteria = Sort.by(Sort.Direction.DESC, "endValidity"); + issuedAc = certificateRepository.findByDeviceIdAndLdevID(device.getId(), ldevID, + sortCriteria); + + generateCertificate = ldevID ? policySettings.isIssueDevIdCertificateEnabled() + : policySettings.isIssueAttestationCertificateEnabled(); + + if (issuedAc != null && !issuedAc.isEmpty() + && (ldevID ? policySettings.isGenerateDevIdCertificateOnExpiration() + : policySettings.isGenerateAttestationCertificateOnExpiration())) { + if (issuedAc.getFirst().getEndValidity().after(currentDate)) { + // so the issued AC is not expired + // however are we within the threshold + days = ProvisionUtils.daysBetween(currentDate, issuedAc.getFirst().getEndValidity()); + generateCertificate = + days < (ldevID ? policySettings.getDevIdReissueThreshold() + : policySettings.getReissueThreshold()); + } + } + + if (generateCertificate) { + attCert.setDeviceId(device.getId()); + attCert.setDeviceName(device.getName()); + certificateRepository.save(attCert); + } + } catch (Exception e) { + log.error("Error saving generated Attestation Certificate to database.", e); + throw new CertificateProcessingException( + "Encountered error while storing Attestation Certificate: " + + e.getMessage(), e); + } + + return generateCertificate; + } } diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/IdentityClaimProcessor.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/IdentityClaimProcessor.java index fae13b497..d1ce6dc1d 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/IdentityClaimProcessor.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/IdentityClaimProcessor.java @@ -37,6 +37,7 @@ import hirs.attestationca.persist.enums.AppraisalStatus; import hirs.attestationca.persist.enums.PublicKeyAlgorithm; import hirs.attestationca.persist.exceptions.IdentityProcessingException; +import hirs.attestationca.persist.provision.helper.CredentialManagementHelper; import hirs.attestationca.persist.provision.helper.ProvisionUtils; import hirs.attestationca.persist.service.SupplyChainValidationService; import hirs.attestationca.persist.validation.SupplyChainCredentialValidator; @@ -74,7 +75,7 @@ @Service @Log4j2 -public class IdentityClaimProcessor extends AbstractProcessor { +public class IdentityClaimProcessor { /** * Number of bytes to include in the TPM2.0 nonce. */ @@ -90,6 +91,7 @@ public class IdentityClaimProcessor extends AbstractProcessor { private final ReferenceManifestRepository referenceManifestRepository; private final ReferenceDigestValueRepository referenceDigestValueRepository; private final DeviceRepository deviceRepository; + private final PolicyRepository policyRepository; private final TPM2ProvisionerStateRepository tpm2ProvisionerStateRepository; private final PublicKeyAlgorithm publicKeyAlgorithm; @@ -127,7 +129,7 @@ public IdentityClaimProcessor( this.deviceRepository = deviceRepository; this.tpm2ProvisionerStateRepository = tpm2ProvisionerStateRepository; this.publicKeyAlgorithm = PublicKeyAlgorithm.fromString(publicKeyAlgorithmStr); - setPolicyRepository(policyRepository); + this.policyRepository = policyRepository; } /** @@ -148,7 +150,6 @@ public byte[] processIdentityClaimTpm2(final byte[] identityClaim) { throw new IllegalArgumentException(errorMsg); } - final PolicyRepository policyRepository = this.getPolicyRepository(); final PolicySettings policySettings = policyRepository.findByName("Default"); // attempt to deserialize Protobuf IdentityClaim @@ -283,10 +284,10 @@ private AppraisalStatus.Status doSupplyChainValidation( // attempt to find an endorsement credential to validate EndorsementCredential endorsementCredential = - parseEcFromIdentityClaim(claim, ekPub, certificateRepository); + CredentialManagementHelper.parseEcFromIdentityClaim(claim, ekPub, certificateRepository); // attempt to find platform credentials to validate - List platformCredentials = parsePcsFromIdentityClaim(claim, + List platformCredentials = CredentialManagementHelper.parsePcsFromIdentityClaim(claim, endorsementCredential, certificateRepository); // Parse and save device info diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/helper/CredentialManagementHelper.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/helper/CredentialManagementHelper.java index 86a8ea5f2..98c4369be 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/helper/CredentialManagementHelper.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/helper/CredentialManagementHelper.java @@ -1,11 +1,17 @@ package hirs.attestationca.persist.provision.helper; +import com.google.protobuf.ByteString; +import hirs.attestationca.configuration.provisionerTpm2.ProvisionerTpm2; import hirs.attestationca.persist.DBManagerException; import hirs.attestationca.persist.entity.manager.CertificateRepository; +import hirs.attestationca.persist.entity.userdefined.Certificate; import hirs.attestationca.persist.entity.userdefined.certificate.EndorsementCredential; import hirs.attestationca.persist.entity.userdefined.certificate.PlatformCredential; import lombok.extern.log4j.Log4j2; +import java.io.IOException; +import java.security.PublicKey; +import java.util.LinkedList; import java.util.List; @@ -162,4 +168,141 @@ public static PlatformCredential storePlatformCredential( + "this method will return a null platform certificate."); return null; } + + /** + * Helper method to parse an Endorsement Credential from a Protobuf generated + * IdentityClaim. Will also check if the Endorsement Credential was already uploaded. + * Persists the Endorsement Credential if it does not already exist. + * + * @param identityClaim a Protobuf generated Identity Claim object + * @param ekPub the endorsement public key from the Identity Claim object + * @param certificateRepository db connector from certificates + * @return the Endorsement Credential, if one exists, null otherwise + */ + public static EndorsementCredential parseEcFromIdentityClaim( + final ProvisionerTpm2.IdentityClaim identityClaim, + final PublicKey ekPub, final CertificateRepository certificateRepository) { + EndorsementCredential endorsementCredential = null; + + if (identityClaim.hasEndorsementCredential()) { + endorsementCredential = CredentialManagementHelper.storeEndorsementCredential( + certificateRepository, + identityClaim.getEndorsementCredential().toByteArray(), + identityClaim.getDv().getNw().getHostname()); + } else if (ekPub != null) { + log.warn("Endorsement Cred was not in the identity claim from the client." + + " Checking for uploads."); + endorsementCredential = getEndorsementCredential(ekPub, certificateRepository); + } else { + log.warn("No endorsement credential was received in identity claim and no EK Public" + + " Key was provided to check for uploaded certificates."); + } + + return endorsementCredential; + } + + /** + * Helper method to parse a set of Platform Credentials from a Protobuf generated + * IdentityClaim and Endorsement Credential. Persists the Platform Credentials if they + * do not already exist. + * + * @param identityClaim a Protobuf generated Identity Claim object + * @param endorsementCredential an endorsement credential to check if platform credentials + * exist + * @param certificateRepository db connector from certificates + * @return the List of Platform Credentials, if they exist, an empty set otherwise + */ + public static List parsePcsFromIdentityClaim( + final ProvisionerTpm2.IdentityClaim identityClaim, + final EndorsementCredential endorsementCredential, + final CertificateRepository certificateRepository) { + List platformCredentials = new LinkedList<>(); + + if (identityClaim.getPlatformCredentialCount() > 0) { + + List platformCredentialList = identityClaim.getPlatformCredentialList(); + + for (ByteString platformCredential : platformCredentialList) { + if (!platformCredential.isEmpty()) { + PlatformCredential storedPlatformCredential = + CredentialManagementHelper.storePlatformCredential( + certificateRepository, platformCredential.toByteArray(), + identityClaim.getDv().getNw().getHostname()); + + if (storedPlatformCredential != null) { + platformCredentials.add(storedPlatformCredential); + } + } + } + } else if (endorsementCredential != null) { + // if none in the identity claim, look for uploaded platform credentials + log.warn("PC was not in the identity claim from the client. Checking for uploads."); + platformCredentials.addAll(getPlatformCredentials(certificateRepository, endorsementCredential)); + } else { + log.warn("No platform credential received in identity claim."); + } + + return platformCredentials; + } + + /** + * 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 static EndorsementCredential getEndorsementCredential( + final PublicKey ekPublicKey, + final CertificateRepository certificateRepository) { + log.debug("Searching for endorsement credential based on public key: {}", ekPublicKey); + + if (ekPublicKey == null) { + throw new IllegalArgumentException("Cannot look up an EC given a null public key"); + } + + EndorsementCredential credential = null; + + try { + credential = certificateRepository.findByPublicKeyModulusHexValue( + Certificate.getPublicKeyModulus(ekPublicKey).toString(Certificate.HEX_BASE)); + } catch (IOException e) { + log.error("Could not extract public key modulus", e); + } + + if (credential == null) { + log.warn("Unable to find endorsement credential for public key."); + } else { + log.debug("Endorsement credential found."); + } + + return credential; + } + + /** + * Helper method that retrieves all the platform credentials associated with the provided Endorsement Credential. + * + * @param certificateRepository certificateRepository + * @param ec endorsement credential + * @return list of platform credentials + */ + private static List getPlatformCredentials(final CertificateRepository certificateRepository, + final EndorsementCredential ec) { + List 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()); + } + } + + return credentials; + } } diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/helper/ProvisionUtils.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/helper/ProvisionUtils.java index c6bc14e25..b17eb5651 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/helper/ProvisionUtils.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/helper/ProvisionUtils.java @@ -175,13 +175,13 @@ public static PublicKey parsePublicKeyFromPublicDataSegment(final PublicKeyAlgor /** * Parses the RSA public key from public data segment generated by TPM 2.0. * - * @param publicAreaSegment the public area segment to parse + * @param publicDataSegment the public area segment to parse * @return the RSA public key of the supplied public data */ - public static RSAPublicKey parseRSAKeyFromPublicDataSegment(final byte[] publicAreaSegment) { - final int publicAreaSegmentLen = publicAreaSegment.length; + public static RSAPublicKey parseRSAKeyFromPublicDataSegment(final byte[] publicDataSegment) { + final int publicDataSegmentLen = publicDataSegment.length; - if (publicAreaSegmentLen < DEFAULT_RSA_MODULUS_LENGTH_IN_BYTES) { + if (publicDataSegmentLen < DEFAULT_RSA_MODULUS_LENGTH_IN_BYTES) { final String errorMsg = "Could not parse RSA Public Key due to public data segment not being long enough."; log.error(errorMsg); throw new IllegalArgumentException(errorMsg); @@ -189,8 +189,8 @@ public static RSAPublicKey parseRSAKeyFromPublicDataSegment(final byte[] publicA // public data ends with 256 byte modulus byte[] modulus = - HexUtils.subarray(publicAreaSegment, publicAreaSegmentLen - DEFAULT_RSA_MODULUS_LENGTH_IN_BYTES, - publicAreaSegmentLen - 1); + HexUtils.subarray(publicDataSegment, publicDataSegmentLen - DEFAULT_RSA_MODULUS_LENGTH_IN_BYTES, + publicDataSegmentLen - 1); return assembleRSAPublicKey(modulus); } @@ -229,21 +229,21 @@ public static RSAPublicKey assembleRSAPublicKey(final BigInteger modulus) { * todo * Parses the ECC public key from public data segment generated by TPM 2.0. * - * @param publicAreaSegment the public area segment to parse + * @param publicDataSegment the public area segment to parse * @return the ECC public key of the supplied public data */ - public static ECPublicKey parseECCKeyFromPublicDataSegment(final byte[] publicAreaSegment) { - final int publicAreaSegmentLen = publicAreaSegment.length; + public static ECPublicKey parseECCKeyFromPublicDataSegment(final byte[] publicDataSegment) { + final int publicDataSegmentLen = publicDataSegment.length; - if (publicAreaSegmentLen < DEFAULT_ECC_KEY_LENGTH_IN_BYTES) { + if (publicDataSegmentLen < DEFAULT_ECC_KEY_LENGTH_IN_BYTES) { final String errorMsg = "Could not parse ECC Public Key due to public data segment not being long enough."; log.error(errorMsg); throw new IllegalArgumentException(errorMsg); } byte[] eccKeySection = - HexUtils.subarray(publicAreaSegment, publicAreaSegmentLen - DEFAULT_ECC_KEY_LENGTH_IN_BYTES, - publicAreaSegmentLen - 1); + HexUtils.subarray(publicDataSegment, publicDataSegmentLen - DEFAULT_ECC_KEY_LENGTH_IN_BYTES, + publicDataSegmentLen - 1); return assembleECCPublicKey(eccKeySection); } diff --git a/HIRS_AttestationCA/src/test/java/hirs/attestationca/persist/AttestationCertificateAuthorityServiceTest.java b/HIRS_AttestationCA/src/test/java/hirs/attestationca/persist/AttestationCertificateAuthorityServiceTest.java index 0ceca17a4..f85de7d7c 100644 --- a/HIRS_AttestationCA/src/test/java/hirs/attestationca/persist/AttestationCertificateAuthorityServiceTest.java +++ b/HIRS_AttestationCA/src/test/java/hirs/attestationca/persist/AttestationCertificateAuthorityServiceTest.java @@ -1,6 +1,8 @@ package hirs.attestationca.persist; import hirs.attestationca.persist.exceptions.CertificateProcessingException; +import hirs.attestationca.persist.provision.AttestationCertificateAuthorityService; +import hirs.attestationca.persist.provision.AttestationCertificateAuthorityServiceImpl; import hirs.attestationca.persist.provision.CertificateRequestProcessor; import hirs.attestationca.persist.provision.IdentityClaimProcessor; import hirs.attestationca.persist.provision.helper.ProvisionUtils; diff --git a/package/linux/aca/aca_bootRun.sh b/package/linux/aca/aca_bootRun.sh index b469b0894..d1228d2c1 100755 --- a/package/linux/aca/aca_bootRun.sh +++ b/package/linux/aca/aca_bootRun.sh @@ -112,10 +112,10 @@ echo "Starting HIRS ACA on https://localhost:8443/HIRS_AttestationCAPortal/porta source /etc/hirs/aca/aca.properties; -# Run the embedded tomcat server with Web TLS enabled and database client TLS enabled by overrding critical parameters +# Run the embedded tomcat server with Web TLS enabled and database client TLS enabled by overriding critical parameters # Note "&" is a sub parameter continuation, space represents a new parameter. Spaces and quotes matter. -# hibernate.connection.url is used fo r the DB connector which established DB TLS connectivity -# server.ssl arguments support the embeded tomcats use of TLS for the ACA Portal +# hibernate.connection.url is used for the DB connector which established DB TLS connectivity +# server.ssl arguments support the embedded tomcats use of TLS for the ACA Portal CONNECTOR_PARAMS="--hibernate.connection.url=jdbc:mariadb://localhost:3306/hirs_db?autoReconnect=true&\ user=$hirs_db_username&\ password=$hirs_db_password&\ @@ -123,7 +123,7 @@ sslMode=VERIFY_CA&\ serverSslCert=$CERT_CHAIN&\ keyStoreType=PKCS12&\ keyStorePassword=$hirs_pki_password&\ -keyStore="$CLIENT_DB_P12" " +keyStore=$CLIENT_DB_P12 " WEB_TLS_PARAMS="--server.ssl.key-store-password=$hirs_pki_password \ --server.ssl.trust-store-password=$hirs_pki_password" diff --git a/package/linux/aca/aca_setup.sh b/package/linux/aca/aca_setup.sh index 91b75a49d..054a1d5e2 100755 --- a/package/linux/aca/aca_setup.sh +++ b/package/linux/aca/aca_setup.sh @@ -144,7 +144,7 @@ fi mkdir -p $HIRS_CONF_DIR $LOG_DIR $HIRS_JSON_DIR $ACA_OPT_DIR touch "$LOG_FILE" -pushd "$SCRIPT_DIR" &>/dev/null +pushd "$SCRIPT_DIR" &>/dev/null || echo "Unable to push directory to stack" # Check if build environment is being used and set up property files if [ -f $PROP_FILE ]; then cp -n $PROP_FILE $HIRS_CONF_DIR/ @@ -242,4 +242,4 @@ fi echo "ACA setup complete" | tee -a "$LOG_FILE" -popd &>/dev/null +popd &>/dev/null || echo "Unable to pop directory from the stack" From a0a49b4d9896babbffbe4b8e13ce44bafdc1961e Mon Sep 17 00:00:00 2001 From: ThatSilentCoder <184309164+ThatSilentCoder@users.noreply.github.com> Date: Tue, 3 Mar 2026 16:12:46 -0500 Subject: [PATCH 10/25] v3.1_issue_1090: Converted helper class to service, started creating services to reduce the lines of code in some of the bigger processors (and to separate responsibilities). Removed public key algorithm property in app.properties. Can now extract algo from the alg id inside of a byte array. Added a new property to Public Key Algorithm enum. --- .../persist/enums/PublicKeyAlgorithm.java | 34 +- ...ionCertificateAuthorityRestController.java | 1 + .../provision/IdentityClaimProcessor.java | 896 ------------------ .../provision/helper/ProvisionUtils.java | 37 +- ...ttestationCertificateAuthorityService.java | 2 +- ...tationCertificateAuthorityServiceImpl.java | 2 +- .../CertificateRequestProcessor.java | 133 +-- .../CredentialManagementService.java} | 259 +++-- .../service/DeviceManagementService.java | 579 +++++++++++ .../service/IdentityClaimProcessor.java | 374 ++++++++ .../service/SupplyChainValidationService.java | 3 +- ...tationCertificateAuthorityServiceTest.java | 19 +- ...a => CredentialManagementServiceTest.java} | 53 +- .../src/main/resources/application.properties | 1 - .../main/resources/application.win.properties | 1 - package/linux/aca/aca_setup.sh | 3 - 16 files changed, 1235 insertions(+), 1162 deletions(-) delete mode 100644 HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/IdentityClaimProcessor.java rename HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/{ => service}/AttestationCertificateAuthorityService.java (94%) rename HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/{ => service}/AttestationCertificateAuthorityServiceImpl.java (97%) rename HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/{ => service}/CertificateRequestProcessor.java (81%) rename HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/{helper/CredentialManagementHelper.java => service/CredentialManagementService.java} (61%) create mode 100644 HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/service/DeviceManagementService.java create mode 100644 HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/service/IdentityClaimProcessor.java rename HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/{ => provision}/service/SupplyChainValidationService.java (99%) rename HIRS_AttestationCA/src/test/java/hirs/attestationca/persist/provision/helper/{CredentialManagementHelperTest.java => CredentialManagementServiceTest.java} (60%) diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/enums/PublicKeyAlgorithm.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/enums/PublicKeyAlgorithm.java index 8f1eeeb62..243e56fe2 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/enums/PublicKeyAlgorithm.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/enums/PublicKeyAlgorithm.java @@ -13,8 +13,9 @@ *

  • {@link #ECC} – Elliptic Curve (EC) public-key algorithm, commonly referred to as ECC.
  • * *

    - * Each enum constant also holds its corresponding JCA (Java Cryptography Architecture) - * standard name, which can be used when creating KeyFactory or Cipher instances. + * Each enum constant holds the public key algorithm name and id. The ID is listed on the + * + * TCG Algorithm Registry Version PDF. */ @Getter @AllArgsConstructor @@ -23,22 +24,26 @@ public enum PublicKeyAlgorithm { /** * RSA Public Key Algorithm. */ - RSA("RSA"), + RSA(0x0001, "RSA"), /** * ECC Public Key Algorithm. */ - ECC("ECC"), + ECC(0x0023, "ECC"), /** * Represents an unknown public key algorithm. * This is used when the application encounters a public key algorithm that is not recognized or supported. - * It may occur if the algorithm is unsupported or not implemented in the current version of the application. */ - UNKNOWN("UNKNOWN"); + UNKNOWN(0xFFFF, "UNKNOWN"); /** - * The name of the cryptographic algorithm. + * The hexadecimal representation of the algorithm ID. + */ + private final int algorithmId; + + /** + * The name of the algorithm. */ private final String algorithmName; @@ -56,5 +61,20 @@ public static PublicKeyAlgorithm fromString(final String algorithmAsString) { } return UNKNOWN; // Return UNKNOWN if no match is found } + + /** + * Retrieves the enum by the algorithm ID. + * + * @param algorithmId algorithm ID + * @return ENUM representation of the public key algorithm + */ + public static PublicKeyAlgorithm fromId(final int algorithmId) { + for (PublicKeyAlgorithm algo : values()) { + if (algo.getAlgorithmId() == algorithmId) { + return algo; + } + } + return UNKNOWN; // If no match found, return UNKNOWN + } } diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/AttestationCertificateAuthorityRestController.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/AttestationCertificateAuthorityRestController.java index d61680f78..0c0e728e1 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/AttestationCertificateAuthorityRestController.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/AttestationCertificateAuthorityRestController.java @@ -1,5 +1,6 @@ package hirs.attestationca.persist.provision; +import hirs.attestationca.persist.provision.service.AttestationCertificateAuthorityService; import lombok.extern.log4j.Log4j2; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/IdentityClaimProcessor.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/IdentityClaimProcessor.java deleted file mode 100644 index d1ce6dc1d..000000000 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/IdentityClaimProcessor.java +++ /dev/null @@ -1,896 +0,0 @@ -package hirs.attestationca.persist.provision; - -import com.google.protobuf.ByteString; -import com.google.protobuf.InvalidProtocolBufferException; -import com.google.protobuf.util.JsonFormat; -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.certificate.attributes.V2.ComponentIdentifierV2; -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; -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.rim.BaseReferenceManifest; -import hirs.attestationca.persist.entity.userdefined.rim.EventLogMeasurements; -import hirs.attestationca.persist.entity.userdefined.rim.ReferenceDigestValue; -import hirs.attestationca.persist.entity.userdefined.rim.SupportReferenceManifest; -import hirs.attestationca.persist.enums.AppraisalStatus; -import hirs.attestationca.persist.enums.PublicKeyAlgorithm; -import hirs.attestationca.persist.exceptions.IdentityProcessingException; -import hirs.attestationca.persist.provision.helper.CredentialManagementHelper; -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; -import hirs.utils.tpm.eventlog.TCGEventLog; -import hirs.utils.tpm.eventlog.TpmPcrEvent; -import jakarta.xml.bind.UnmarshalException; -import lombok.extern.log4j.Log4j2; -import org.apache.commons.codec.binary.Hex; -import org.apache.commons.lang3.ArrayUtils; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Service; - -import java.io.IOException; -import java.net.InetAddress; -import java.net.UnknownHostException; -import java.nio.charset.StandardCharsets; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.security.PublicKey; -import java.security.cert.CertificateException; -import java.util.ArrayList; -import java.util.Base64; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.UUID; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -@Service -@Log4j2 -public class IdentityClaimProcessor { - /** - * Number of bytes to include in the TPM2.0 nonce. - */ - public static final int NONCE_LENGTH = 20; - private static final String PCR_QUOTE_MASK = "0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23"; - private static final int NUM_OF_VARIABLES = 5; - private static final int MAC_BYTES = 6; - - private final SupplyChainValidationService supplyChainValidationService; - private final CertificateRepository certificateRepository; - private final ComponentResultRepository componentResultRepository; - private final ComponentInfoRepository componentInfoRepository; - private final ReferenceManifestRepository referenceManifestRepository; - private final ReferenceDigestValueRepository referenceDigestValueRepository; - private final DeviceRepository deviceRepository; - private final PolicyRepository policyRepository; - private final TPM2ProvisionerStateRepository tpm2ProvisionerStateRepository; - private final PublicKeyAlgorithm publicKeyAlgorithm; - - /** - * Constructor. - * - * @param supplyChainValidationService supply chain validation service - * @param certificateRepository certificate repository - * @param componentResultRepository component result repository - * @param componentInfoRepository component info repository - * @param referenceManifestRepository reference manifest repository - * @param referenceDigestValueRepository reference digest value repository - * @param deviceRepository device repository - * @param tpm2ProvisionerStateRepository tpm2 provisioner state repository - * @param policyRepository policy repository - */ - @Autowired - public IdentityClaimProcessor( - @Value("${aca.current.public.key.algorithm}") final String publicKeyAlgorithmStr, - final SupplyChainValidationService supplyChainValidationService, - final CertificateRepository certificateRepository, - final ComponentResultRepository componentResultRepository, - final ComponentInfoRepository componentInfoRepository, - final ReferenceManifestRepository referenceManifestRepository, - final ReferenceDigestValueRepository referenceDigestValueRepository, - final DeviceRepository deviceRepository, - final TPM2ProvisionerStateRepository tpm2ProvisionerStateRepository, - final PolicyRepository policyRepository) { - this.supplyChainValidationService = supplyChainValidationService; - this.certificateRepository = certificateRepository; - this.componentResultRepository = componentResultRepository; - this.componentInfoRepository = componentInfoRepository; - this.referenceManifestRepository = referenceManifestRepository; - this.referenceDigestValueRepository = referenceDigestValueRepository; - this.deviceRepository = deviceRepository; - this.tpm2ProvisionerStateRepository = tpm2ProvisionerStateRepository; - this.publicKeyAlgorithm = PublicKeyAlgorithm.fromString(publicKeyAlgorithmStr); - this.policyRepository = policyRepository; - } - - /** - * Basic implementation of the ACA processIdentityClaimTpm2 method. Parses the claim, - * stores the device info, performs supply chain validation, generates a nonce, - * and wraps that nonce with the make credential process before returning it to the client. - * attCert.setPcrValues(pcrValues); - * - * @param identityClaim the request to process, cannot be null - * @return an identity claim response for the specified request containing a wrapped blob - */ - public byte[] processIdentityClaimTpm2(final byte[] identityClaim) { - log.info("Identity Claim has been received and is ready to be processed"); - - if (ArrayUtils.isEmpty(identityClaim)) { - final String errorMsg = "The IdentityClaim sent by the client cannot be null or empty."; - log.error(errorMsg); - throw new IllegalArgumentException(errorMsg); - } - - final PolicySettings policySettings = policyRepository.findByName("Default"); - - // attempt to deserialize Protobuf IdentityClaim - ProvisionerTpm2.IdentityClaim claim = ProvisionUtils.parseIdentityClaim(identityClaim); - - String identityClaimJsonString = ""; - try { - identityClaimJsonString = JsonFormat.printer().print(claim); - } catch (InvalidProtocolBufferException exception) { - log.error("Identity claim could not be parsed properly into a json string"); - } - - // parse the EK Public key from the IdentityClaim once for use in supply chain validation - // and later tpm20MakeCredential function - PublicKey endorsementCredentialPublicKey = - ProvisionUtils.parsePublicKeyFromPublicDataSegment(publicKeyAlgorithm, - claim.getEkPublicArea().toByteArray()); - - AppraisalStatus.Status validationResult = AppraisalStatus.Status.FAIL; - - try { - validationResult = doSupplyChainValidation(claim, endorsementCredentialPublicKey); - } catch (Exception ex) { - log.error(ex.getMessage()); - } - - ByteString blobStr = ByteString.copyFrom(new byte[]{}); - - if (validationResult == AppraisalStatus.Status.PASS) { - PublicKey akPub = ProvisionUtils.parsePublicKeyFromPublicDataSegment(publicKeyAlgorithm, - claim.getAkPublicArea().toByteArray()); - byte[] nonce = ProvisionUtils.generateRandomBytes(NONCE_LENGTH); - blobStr = ProvisionUtils.tpm20MakeCredential(publicKeyAlgorithm, endorsementCredentialPublicKey, akPub, - nonce); - - String pcrQuoteMask = PCR_QUOTE_MASK; - - String strNonce = HexUtils.byteArrayToHexString(nonce); - log.info("Sending nonce: {}", strNonce); - log.info("Persisting identity claim of length: {}", identityClaim.length); - - tpm2ProvisionerStateRepository.save(new TPM2ProvisionerState(nonce, identityClaim)); - - if (policySettings.isIgnoreImaEnabled()) { - pcrQuoteMask = PCR_QUOTE_MASK.replace("10,", ""); - } - - // Package response - ProvisionerTpm2.IdentityClaimResponse identityClaimResponse - = ProvisionerTpm2.IdentityClaimResponse.newBuilder() - .setCredentialBlob(blobStr).setPcrMask(pcrQuoteMask) - .setStatus(ProvisionerTpm2.ResponseStatus.PASS) - .build(); - - String identityClaimResponseJsonStringAfterSuccess = ""; - try { - identityClaimResponseJsonStringAfterSuccess = - JsonFormat.printer().print(identityClaimResponse); - } catch (InvalidProtocolBufferException exception) { - log.error("Identity claim response after a successful validation " - + "could not be parsed properly into a json string"); - } - - if (!policySettings.isSaveProtobufToLogNeverEnabled() - && policySettings.isSaveProtobufToLogAlwaysEnabled()) { - - log.info("----------------- Start Of Protobuf Logging Of Identity Claim/Response " - + " After Successful Validation -----------------"); - - log.info("Identity Claim object received after a " - + "successful validation: {}", identityClaimJsonString.isEmpty() - ? claim : identityClaimJsonString); - - log.info("Identity Claim Response object after a " - + "successful validation: {}", identityClaimResponseJsonStringAfterSuccess.isEmpty() - ? identityClaimResponse : identityClaimResponseJsonStringAfterSuccess); - - log.info("----------------- End Of Protobuf Logging Of Identity Claim/Response " - + " After Successful Validation -----------------"); - } - - return identityClaimResponse.toByteArray(); - } else { - log.error("Supply chain validation did not succeed. Result is: {}", validationResult); - // empty response - ProvisionerTpm2.IdentityClaimResponse identityClaimResponse - = ProvisionerTpm2.IdentityClaimResponse.newBuilder() - .setCredentialBlob(blobStr) - .setStatus(ProvisionerTpm2.ResponseStatus.FAIL) - .build(); - - String identityClaimResponseJsonStringAfterFailure = ""; - try { - identityClaimResponseJsonStringAfterFailure = - JsonFormat.printer().print(identityClaimResponse); - } catch (InvalidProtocolBufferException exception) { - log.error("Identity claim response after a failed validation " - + "could not be parsed properly into a json string"); - } - - if (!policySettings.isSaveProtobufToLogNeverEnabled() - && (policySettings.isSaveProtobufToLogAlwaysEnabled() - || policySettings.isSaveProtobufToLogOnFailedValEnabled())) { - log.info("----------------- Start Of Protobuf Logging Of Identity Claim/Response " - + " After Failed Validation -----------------"); - - log.info("Identity Claim object received after a " - + "failed validation: {}", identityClaimJsonString.isEmpty() - ? claim : identityClaimJsonString); - - log.info("Identity Claim Response object after a " - + "failed validation: {}", identityClaimResponseJsonStringAfterFailure.isEmpty() - ? identityClaimResponse : identityClaimResponseJsonStringAfterFailure); - - log.info("----------------- End Of Protobuf Logging Of Identity Claim/Response " - + " After Failed Validation -----------------"); - } - - return identityClaimResponse.toByteArray(); - } - } - - /** - * Performs supply chain validation. - * - * @param claim the identity claim - * @param ekPub the public endorsement key - * @return the {@link AppraisalStatus} of the supply chain validation - */ - private AppraisalStatus.Status doSupplyChainValidation( - final ProvisionerTpm2.IdentityClaim claim, final PublicKey ekPub) throws IOException { - - // attempt to find an endorsement credential to validate - EndorsementCredential endorsementCredential = - CredentialManagementHelper.parseEcFromIdentityClaim(claim, ekPub, certificateRepository); - - // attempt to find platform credentials to validate - List platformCredentials = CredentialManagementHelper.parsePcsFromIdentityClaim(claim, - endorsementCredential, certificateRepository); - - // Parse and save device info - Device device = processDeviceInfo(claim); - -// 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 - // additional information from the DB if information exists - if (platformCredentials.size() == 1) { - List tempList = new LinkedList<>(); - for (PlatformCredential pc : platformCredentials) { - if (pc != null && pc.getPlatformSerial() != null) { - tempList.addAll(certificateRepository - .byBoardSerialNumber(pc.getPlatformSerial())); - } - } - - platformCredentials.addAll(tempList); - } - - // store component results objects - for (PlatformCredential platformCredential : platformCredentials) { - List componentResults = componentResultRepository - .findByCertificateSerialNumberAndBoardSerialNumber( - platformCredential.getSerialNumber().toString(), - platformCredential.getPlatformSerial()); - - if (componentResults.isEmpty()) { - savePlatformComponents(platformCredential); - } else { - componentResults.forEach((componentResult) -> { - componentResult.restore(); - componentResult.resetCreateTime(); - componentResultRepository.save(componentResult); - }); - } - } - - // perform supply chain validation - SupplyChainValidationSummary summary = supplyChainValidationService.validateSupplyChain( - endorsementCredential, platformCredentials, device, - componentInfoRepository.findByDeviceName(device.getName())); - device.setSummaryId(summary.getId().toString()); - - // update the validation result in the device - AppraisalStatus.Status validationResult = summary.getOverallValidationResult(); - device.setSupplyChainValidationStatus(validationResult); - this.deviceRepository.save(device); - - return validationResult; - } - - /** - * Helper method that utilizes the identity claim to produce a device info report. - * - * @param claim identity claim - * @return device info - */ - private Device processDeviceInfo(final ProvisionerTpm2.IdentityClaim claim) { - DeviceInfoReport deviceInfoReport = null; - - try { - deviceInfoReport = parseDeviceInfo(claim); - } catch (NoSuchAlgorithmException noSaEx) { - log.error(noSaEx); - } - - if (deviceInfoReport == null) { - log.error("Failed to deserialize Device Info Report"); - throw new IdentityProcessingException("Device Info Report failed to deserialize " - + "from Identity Claim"); - } - - log.info("Processing Device Info Report"); - - // store device and device info report. - 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); - } - device.setDeviceInfo(deviceInfoReport); - return this.deviceRepository.save(device); - } - - /** - * Converts a protobuf DeviceInfo object to a HIRS Utils DeviceInfoReport object. - * - * @param claim the protobuf serialized identity claim containing the device info - * @return a HIRS Utils DeviceInfoReport representation of device info - */ - private DeviceInfoReport parseDeviceInfo(final ProvisionerTpm2.IdentityClaim claim) - throws NoSuchAlgorithmException { - ProvisionerTpm2.DeviceInfo dv = claim.getDv(); - String pcrValues = ""; - - // Get network info - ProvisionerTpm2.NetworkInfo nwProto = dv.getNw(); - - InetAddress ip = null; - try { - ip = InetAddress.getByName(nwProto.getIpAddress()); - } catch (UnknownHostException uhEx) { - log.error("Unable to parse IP address: ", uhEx); - } - String[] macAddressParts = nwProto.getMacAddress().split(":"); - - // convert mac hex string to byte values - byte[] macAddressBytes = new byte[MAC_BYTES]; - Integer hex; - if (macAddressParts.length == MAC_BYTES) { - for (int i = 0; i < MAC_BYTES; i++) { - hex = HexUtils.hexToInt(macAddressParts[i]); - macAddressBytes[i] = hex.byteValue(); - } - } - - NetworkInfo nw = new NetworkInfo(nwProto.getHostname(), ip, macAddressBytes); - - // Get firmware info - ProvisionerTpm2.FirmwareInfo fwProto = dv.getFw(); - FirmwareInfo fw = new FirmwareInfo(fwProto.getBiosVendor(), fwProto.getBiosVersion(), - fwProto.getBiosReleaseDate()); - - // Get OS info - ProvisionerTpm2.OsInfo osProto = dv.getOs(); - OSInfo os = new OSInfo(osProto.getOsName(), osProto.getOsVersion(), osProto.getOsArch(), - osProto.getDistribution(), osProto.getDistributionRelease()); - - // Get hardware info - ProvisionerTpm2.HardwareInfo hwProto = dv.getHw(); - - // Make sure chassis info has at least one chassis - String firstChassisSerialNumber = DeviceInfoEnums.NOT_SPECIFIED; - if (hwProto.getChassisInfoCount() > 0) { - firstChassisSerialNumber = hwProto.getChassisInfo(0).getSerialNumber(); - } - - // Make sure baseboard info has at least one baseboard - String firstBaseboardSerialNumber = DeviceInfoEnums.NOT_SPECIFIED; - if (hwProto.getBaseboardInfoCount() > 0) { - firstBaseboardSerialNumber = hwProto.getBaseboardInfo(0).getSerialNumber(); - } - - HardwareInfo hw = new HardwareInfo(hwProto.getManufacturer(), hwProto.getProductName(), - hwProto.getProductVersion(), hwProto.getSystemSerialNumber(), - firstChassisSerialNumber, firstBaseboardSerialNumber); - - if (dv.hasPcrslist()) { - pcrValues = dv.getPcrslist().toStringUtf8(); - } - - // 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()); - BaseReferenceManifest baseRim = null; - SupportReferenceManifest supportRim = null; - EventLogMeasurements integrityMeasurements; - boolean isReplacement = false; - String replacementRimId = ""; - String tagId = ""; - String fileName = ""; - Pattern pattern = Pattern.compile("([^\\s]+(\\.(?i)(rimpcr|rimel|bin|log))$)"); - Matcher matcher; - MessageDigest messageDigest = MessageDigest.getInstance("SHA-256"); - - if (dv.getSwidfileCount() > 0) { - for (ByteString swidFile : dv.getSwidfileList()) { - try { - baseRim = (BaseReferenceManifest) referenceManifestRepository - .findByBase64Hash(Base64.getEncoder() - .encodeToString(messageDigest - .digest(swidFile.toByteArray()))); - if (baseRim == null) { - /* - Either the swidFile does not have a corresponding base RIM in the backend - or it was deleted. Check if there is a replacement by comparing tagId against - all other base RIMs, and then set the corresponding support rim's deviceName. - */ - baseRim = new BaseReferenceManifest( - String.format("%s.swidtag", - defaultClientName), - swidFile.toByteArray()); - List baseRims = referenceManifestRepository.findAllBaseRims(); - for (BaseReferenceManifest bRim : baseRims) { - if (bRim.getTagId().equals(baseRim.getTagId())) { - baseRim = bRim; - replacementRimId = baseRim.getAssociatedRim().toString(); - isReplacement = true; - break; - } - } - baseRim.setDeviceName(dv.getNw().getHostname()); - this.referenceManifestRepository.save(baseRim); - } else if (baseRim.isArchived()) { - /* - This block accounts for RIMs that may have been soft-deleted (archived) - in an older version of the ACA. - */ - List rims = referenceManifestRepository.findByArchiveFlag(false); - for (ReferenceManifest rim : rims) { - if (rim.isBase() && rim.getTagId().equals(baseRim.getTagId()) - && rim.getCreateTime().after(baseRim.getCreateTime())) { - baseRim.setDeviceName(null); - baseRim = (BaseReferenceManifest) rim; - baseRim.setDeviceName(dv.getNw().getHostname()); - } - } - if (baseRim.isArchived()) { - throw new Exception("Unable to locate an unarchived base RIM."); - } else { - this.referenceManifestRepository.save(baseRim); - } - } else { - baseRim.setDeviceName(dv.getNw().getHostname()); - this.referenceManifestRepository.save(baseRim); - } - tagId = baseRim.getTagId(); - } catch (UnmarshalException e) { - log.error(e); - } catch (Exception ex) { - log.error("Failed to load base rim: {}", ex.getMessage()); - } - } - } else { - log.warn("{} did not send swid tag file...", dv.getNw().getHostname()); - } - - if (dv.getLogfileCount() > 0) { - for (ByteString logFile : dv.getLogfileList()) { - try { - supportRim = - (SupportReferenceManifest) referenceManifestRepository.findByHexDecHashAndRimType( - Hex.encodeHexString(messageDigest.digest(logFile.toByteArray())), - ReferenceManifest.SUPPORT_RIM); - if (supportRim == null) { - /* - Either the logFile does not have a corresponding support RIM in the backend - or it was deleted. The support RIM for a replacement base RIM is handled - in the previous loop block. - */ - if (isReplacement) { - Optional replacementRim = - referenceManifestRepository.findById(UUID.fromString(replacementRimId)); - if (replacementRim.isPresent()) { - supportRim = (SupportReferenceManifest) replacementRim.get(); - supportRim.setDeviceName(dv.getNw().getHostname()); - } else { - throw new Exception("Unable to locate support RIM " + replacementRimId); - } - } else { - supportRim = new SupportReferenceManifest( - String.format("%s.rimel", - defaultClientName), - logFile.toByteArray()); - // this is a validity check - new TCGEventLog(supportRim.getRimBytes()); - // no issues, continue - supportRim.setPlatformManufacturer(dv.getHw().getManufacturer()); - supportRim.setPlatformModel(dv.getHw().getProductName()); - supportRim.setFileName(String.format("%s_[%s].rimel", defaultClientName, - supportRim.getHexDecHash().substring( - supportRim.getHexDecHash().length() - NUM_OF_VARIABLES))); - } - supportRim.setDeviceName(dv.getNw().getHostname()); - this.referenceManifestRepository.save(supportRim); - } else if (supportRim.isArchived()) { - /* - This block accounts for RIMs that may have been soft-deleted (archived) - in an older version of the ACA. - */ - List rims = referenceManifestRepository.findByArchiveFlag(false); - for (ReferenceManifest rim : rims) { - if (rim.isSupport() - && rim.getTagId().equals(supportRim.getTagId()) - && rim.getCreateTime().after(supportRim.getCreateTime())) { - supportRim.setDeviceName(null); - supportRim = (SupportReferenceManifest) rim; - supportRim.setDeviceName(dv.getNw().getHostname()); - } - } - if (supportRim.isArchived()) { - throw new Exception("Unable to locate an unarchived support RIM."); - } else { - this.referenceManifestRepository.save(supportRim); - } - } else { - supportRim.setDeviceName(dv.getNw().getHostname()); - this.referenceManifestRepository.save(supportRim); - } - } catch (IOException ioEx) { - log.error(ioEx); - } catch (Exception ex) { - log.error("Failed to load support rim: {}", ex.getMessage()); - } - } - } else { - log.warn("{} did not send support RIM file...", dv.getNw().getHostname()); - } - - //update Support RIMs and Base RIMs. - for (ByteString swidFile : dv.getSwidfileList()) { - baseRim = (BaseReferenceManifest) referenceManifestRepository - .findByBase64Hash(Base64.getEncoder().encodeToString(messageDigest.digest( - swidFile.toByteArray()))); - if (baseRim != null) { - // get file name to use - for (SwidResource swid : baseRim.getFileResources()) { - matcher = pattern.matcher(swid.getName()); - if (matcher.matches()) { - //found the file name - int dotIndex = swid.getName().lastIndexOf("."); - fileName = swid.getName().substring(0, dotIndex); - baseRim.setFileName(String.format("%s.swidtag", - fileName)); - } - - // now update support rim - SupportReferenceManifest dbSupport = - (SupportReferenceManifest) referenceManifestRepository - .findByHexDecHashAndRimType(swid.getHashValue(), - ReferenceManifest.SUPPORT_RIM); - if (dbSupport != null) { - dbSupport.setFileName(swid.getName()); - dbSupport.setSwidTagVersion(baseRim.getSwidTagVersion()); - dbSupport.setTagId(baseRim.getTagId()); - dbSupport.setSwidTagVersion(baseRim.getSwidTagVersion()); - dbSupport.setSwidVersion(baseRim.getSwidVersion()); - dbSupport.setSwidPatch(baseRim.isSwidPatch()); - dbSupport.setSwidSupplemental(baseRim.isSwidSupplemental()); - baseRim.setAssociatedRim(dbSupport.getId()); - dbSupport.setUpdated(true); - dbSupport.setAssociatedRim(baseRim.getId()); - this.referenceManifestRepository.save(dbSupport); - } else { - log.warn("Could not locate support RIM with hash {}}", swid.getHashValue()); - } - } - this.referenceManifestRepository.save(baseRim); - } - } - - generateDigestRecords(hw.getManufacturer(), hw.getProductName()); - - if (dv.hasLivelog()) { - log.info("Device sent bios measurement log..."); - fileName = String.format("%s.measurement", - dv.getNw().getHostname()); - try { - EventLogMeasurements deviceLiveLog = new EventLogMeasurements(fileName, - dv.getLivelog().toByteArray()); - // find previous version. - integrityMeasurements = referenceManifestRepository - .byMeasurementDeviceNameUnarchived(dv.getNw().getHostname()); - - if (integrityMeasurements != null) { - // Find previous log and archive it - integrityMeasurements.archive(); - this.referenceManifestRepository.save(integrityMeasurements); - } - - List baseRims = referenceManifestRepository - .getBaseByManufacturerModel(dv.getHw().getManufacturer(), - dv.getHw().getProductName()); - integrityMeasurements = deviceLiveLog; - integrityMeasurements.setPlatformManufacturer(dv.getHw().getManufacturer()); - integrityMeasurements.setPlatformModel(dv.getHw().getProductName()); - if (tagId != null && !tagId.trim().isEmpty()) { - integrityMeasurements.setTagId(tagId); - } - integrityMeasurements.setDeviceName(dv.getNw().getHostname()); - - this.referenceManifestRepository.save(integrityMeasurements); - - for (BaseReferenceManifest bRim : baseRims) { - if (bRim != null) { - // pull the base versions of the swidtag and rimel and set the - // event log hash for use during provision - SupportReferenceManifest sBaseRim = referenceManifestRepository - .getSupportRimEntityById(bRim.getAssociatedRim()); - if (sBaseRim != null) { - bRim.setEventLogHash(deviceLiveLog.getHexDecHash()); - sBaseRim.setEventLogHash(deviceLiveLog.getHexDecHash()); - referenceManifestRepository.save(bRim); - referenceManifestRepository.save(sBaseRim); - } else { - log.warn("Could not locate support RIM associated with " - + "base RIM " + bRim.getId()); - } - } - } - } catch (IOException ioEx) { - log.error(ioEx); - } - } else { - log.warn("{} did not send bios measurement log...", dv.getNw().getHostname()); - } - - // Get TPM info, currently unimplemented - TPMInfo tpmInfo = new TPMInfo(DeviceInfoEnums.NOT_SPECIFIED, - (short) 0, - (short) 0, - (short) 0, - (short) 0, - pcrValues.getBytes(StandardCharsets.UTF_8), - null, null); - - // Create final report - DeviceInfoReport dvReport = new DeviceInfoReport(nw, os, fw, hw, tpmInfo, - claim.getClientVersion()); - dvReport.setPaccorOutputString(claim.getPaccorOutput()); - - return dvReport; - } - - /** - * Helper method that generates digest records using the provided device's manufacturer and model - * information. - * - * @param manufacturer device manufacturer - * @param model device model - * @return boolean that represents that status of the digest records generation - */ - private boolean generateDigestRecords(final String manufacturer, final String model) { - List rdValues = new LinkedList<>(); - SupportReferenceManifest baseSupportRim = null; - List supplementalRims = new ArrayList<>(); - List patchRims = new ArrayList<>(); - List dbSupportRims = this.referenceManifestRepository - .getSupportByManufacturerModel(manufacturer, model); - List expectedValues = referenceDigestValueRepository - .findByManufacturerAndModel(manufacturer, model); - - Map digestValueMap = new HashMap<>(); - expectedValues.forEach((rdv) -> digestValueMap.put(rdv.getDigestValue(), rdv)); - - for (SupportReferenceManifest dbSupport : dbSupportRims) { - if (dbSupport.isSwidPatch()) { - patchRims.add(dbSupport); - } else if (dbSupport.isSwidSupplemental()) { - supplementalRims.add(dbSupport); - } else { - // we have a base support rim (verify this is getting set) - baseSupportRim = dbSupport; - } - } - - if (baseSupportRim != null - && referenceDigestValueRepository.findBySupportRimHash(baseSupportRim.getHexDecHash()) - .isEmpty()) { - try { - TCGEventLog eventLog = new TCGEventLog(baseSupportRim.getRimBytes()); - ReferenceDigestValue rdv; - for (TpmPcrEvent tpe : eventLog.getEventList()) { - rdv = new ReferenceDigestValue(baseSupportRim.getAssociatedRim(), - baseSupportRim.getId(), manufacturer, model, tpe.getPcrIndex(), - tpe.getEventDigestStr(), baseSupportRim.getHexDecHash(), - tpe.getEventTypeStr(), - false, false, true, tpe.getEventContent()); - rdValues.add(rdv); - } - - // since I have the base already I don't have to care about the backward - // linkage - for (SupportReferenceManifest supplemental : supplementalRims) { - eventLog = new TCGEventLog(supplemental.getRimBytes()); - for (TpmPcrEvent tpe : eventLog.getEventList()) { - // all RDVs will have the same base rim - rdv = new ReferenceDigestValue(baseSupportRim.getAssociatedRim(), - supplemental.getId(), manufacturer, model, tpe.getPcrIndex(), - tpe.getEventDigestStr(), baseSupportRim.getHexDecHash(), - tpe.getEventTypeStr(), - false, false, true, tpe.getEventContent()); - rdValues.add(rdv); - } - } - - // Save all supplemental values - ReferenceDigestValue tempRdv; - for (ReferenceDigestValue subRdv : rdValues) { - // check if the value already exists - if (digestValueMap.containsKey(subRdv.getDigestValue())) { - tempRdv = digestValueMap.get(subRdv.getDigestValue()); - if (tempRdv.getPcrIndex() != subRdv.getPcrIndex() - && !tempRdv.getEventType().equals(subRdv.getEventType())) { - referenceDigestValueRepository.save(subRdv); - } else { - // will this be a problem down the line? - referenceDigestValueRepository.save(subRdv); - } - } else { - referenceDigestValueRepository.save(subRdv); - } - digestValueMap.put(subRdv.getDigestValue(), subRdv); - } - - // if a patch value doesn't exist, error? - ReferenceDigestValue dbRdv; - String patchedValue; - for (SupportReferenceManifest patch : patchRims) { - eventLog = new TCGEventLog(patch.getRimBytes()); - for (TpmPcrEvent tpe : eventLog.getEventList()) { - patchedValue = tpe.getEventDigestStr(); - dbRdv = digestValueMap.get(patchedValue); - - if (dbRdv == null) { - log.error("Patching value does not exist ({})", patchedValue); - } else { - // WIP - Until we get patch examples - dbRdv.setPatched(true); - } - } - } - } catch (CertificateException | NoSuchAlgorithmException | IOException ex) { - log.error(ex); - } - } - - return true; - } - - /** - * Helper method that saves the provided platform certificate's components in the database. - * - * @param certificate certificate - */ - private void savePlatformComponents(final Certificate certificate) throws IOException { - PlatformCredential platformCredential; - - if (certificate instanceof PlatformCredential) { - platformCredential = (PlatformCredential) certificate; - ComponentResult componentResult; - - if (platformCredential.getPlatformConfigurationV1() != null) { - List componentIdentifiers = platformCredential - .getComponentIdentifiers(); - - for (ComponentIdentifier componentIdentifier : componentIdentifiers) { - componentResult = new ComponentResult(platformCredential.getPlatformSerial(), - platformCredential.getSerialNumber().toString(), - platformCredential.getPlatformChainType(), - componentIdentifier); - componentResult.setFailedValidation(false); - componentResult.setDelta(!platformCredential.isPlatformBase()); - componentResultRepository.save(componentResult); - } - } else if (platformCredential.getPlatformConfigurationV2() != null) { - List componentIdentifiersV2 = platformCredential - .getComponentIdentifiersV2(); - - for (ComponentIdentifierV2 componentIdentifierV2 : componentIdentifiersV2) { - componentResult = new ComponentResult(platformCredential.getPlatformSerial(), - platformCredential.getSerialNumber().toString(), - platformCredential.getPlatformChainType(), - componentIdentifierV2); - componentResult.setFailedValidation(false); - componentResult.setDelta(!platformCredential.isPlatformBase()); - componentResultRepository.save(componentResult); - } - } - } - } - - /** - * Helper method that attempts to find all the provided device's components. - * - * @param hostName device's host name - * @param paccorString string representation of the paccor tool output - */ - private void handleDeviceComponents(final String hostName, final String paccorString) { - Map componentInfoMap = new HashMap<>(); - - try { - List componentInfos = SupplyChainCredentialValidator - .getComponentInfoFromPaccorOutput(hostName, paccorString); - - // check the DB for like component infos - List dbComponentInfos = this.componentInfoRepository.findByDeviceName(hostName); - dbComponentInfos.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"); - } - } -} diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/helper/ProvisionUtils.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/helper/ProvisionUtils.java index b17eb5651..824066124 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/helper/ProvisionUtils.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/helper/ProvisionUtils.java @@ -159,12 +159,12 @@ public static String getPemEncodedCertificate(final X509Certificate certificate) * Parses a public key from a byte array using one of the two supported public key * algorithm and returns it as a generic PublicKey. * - * @param publicKeyAlgorithm public key algorithm - * @param publicAreaSegment public area segment generated by TPM 2.0 + * @param publicAreaSegment public area segment generated by TPM 2.0 * @return the parsed PublicKey instance */ - public static PublicKey parsePublicKeyFromPublicDataSegment(final PublicKeyAlgorithm publicKeyAlgorithm, - final byte[] publicAreaSegment) { + public static PublicKey parsePublicKeyFromPublicDataSegment(final byte[] publicAreaSegment) { + final PublicKeyAlgorithm publicKeyAlgorithm = determinePublicKeyAlgorithm(publicAreaSegment); + return switch (publicKeyAlgorithm) { case RSA -> parseRSAKeyFromPublicDataSegment(publicAreaSegment); case ECC -> parseECCKeyFromPublicDataSegment(publicAreaSegment); @@ -172,6 +172,30 @@ public static PublicKey parsePublicKeyFromPublicDataSegment(final PublicKeyAlgor }; } + /** + * Extracts the public key algorithm from the first two bytes of the provided byte array. + * + * @param byteArray byte array + * @return public key algorithm + */ + public static PublicKeyAlgorithm determinePublicKeyAlgorithm(byte[] byteArray) { + + // Return UNKNOWN if there are not enough bytes for comparison + if (byteArray == null || byteArray.length < 2) { + return PublicKeyAlgorithm.UNKNOWN; + } + + // Extract the first two bytes from the byte array and combine them into a 16-bit integer (algorithm ID). + // - `byteArray[0] & 0xFF` ensures the first byte is treated as unsigned (0-255 range). + // - `<< 8` shifts the first byte 8 bits to the left, placing it in the higher byte of the 16-bit value. + // - `byteArray[1] & 0xFF` ensures the second byte is treated as unsigned, then it occupies the lower byte. + // - The bitwise OR (`|`) combines the two parts into a single 16-bit value (int). + final int algorithmId = ((byteArray[0] & 0xFF) << 8) | (byteArray[1] & 0xFF); + + // Compare the extracted ID with the enums + return PublicKeyAlgorithm.fromId(algorithmId); + } + /** * Parses the RSA public key from public data segment generated by TPM 2.0. * @@ -268,17 +292,18 @@ public static ECPublicKey assembleECCPublicKey(final byte[] eccKeySection) { } /** - * @param publicKeyAlgorithm public key algorithm * @param endorsementPublicKey endorsement key in the identity claim * @param attestationPublicKey attestation key in the identity claim * @param secret a nonce * @return the encrypted blob forming the identity claim challenge */ public static ByteString tpm20MakeCredential( - final PublicKeyAlgorithm publicKeyAlgorithm, final PublicKey endorsementPublicKey, final PublicKey attestationPublicKey, final byte[] secret) { + + final PublicKeyAlgorithm publicKeyAlgorithm = PublicKeyAlgorithm.RSA; + // check size of the secret if (secret.length > MAX_SECRET_LENGTH) { throw new IllegalArgumentException("Secret must be " + MAX_SECRET_LENGTH diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/AttestationCertificateAuthorityService.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/service/AttestationCertificateAuthorityService.java similarity index 94% rename from HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/AttestationCertificateAuthorityService.java rename to HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/service/AttestationCertificateAuthorityService.java index e1658f29c..18f46ce79 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/AttestationCertificateAuthorityService.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/service/AttestationCertificateAuthorityService.java @@ -1,4 +1,4 @@ -package hirs.attestationca.persist.provision; +package hirs.attestationca.persist.provision.service; /** * Interface that defines the responsibilities of the Attestation Certificate Authority service. diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/AttestationCertificateAuthorityServiceImpl.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/service/AttestationCertificateAuthorityServiceImpl.java similarity index 97% rename from HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/AttestationCertificateAuthorityServiceImpl.java rename to HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/service/AttestationCertificateAuthorityServiceImpl.java index 7b404e7ae..70f6b4cae 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/AttestationCertificateAuthorityServiceImpl.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/service/AttestationCertificateAuthorityServiceImpl.java @@ -1,4 +1,4 @@ -package hirs.attestationca.persist.provision; +package hirs.attestationca.persist.provision.service; import lombok.extern.log4j.Log4j2; import org.springframework.beans.factory.annotation.Autowired; diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/CertificateRequestProcessor.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/service/CertificateRequestProcessor.java similarity index 81% rename from HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/CertificateRequestProcessor.java rename to HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/service/CertificateRequestProcessor.java index a6bcc0d79..cd32cc0aa 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/CertificateRequestProcessor.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/service/CertificateRequestProcessor.java @@ -1,9 +1,8 @@ -package hirs.attestationca.persist.provision; +package hirs.attestationca.persist.provision.service; import com.google.protobuf.InvalidProtocolBufferException; import com.google.protobuf.util.JsonFormat; 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; @@ -12,17 +11,13 @@ import hirs.attestationca.persist.entity.userdefined.PolicySettings; import hirs.attestationca.persist.entity.userdefined.SupplyChainValidationSummary; 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.TPMInfo; import hirs.attestationca.persist.entity.userdefined.report.DeviceInfoReport; import hirs.attestationca.persist.enums.AppraisalStatus; -import hirs.attestationca.persist.enums.PublicKeyAlgorithm; import hirs.attestationca.persist.exceptions.CertificateProcessingException; -import hirs.attestationca.persist.provision.helper.CredentialManagementHelper; import hirs.attestationca.persist.provision.helper.IssuedCertificateAttributeHelper; import hirs.attestationca.persist.provision.helper.ProvisionUtils; -import hirs.attestationca.persist.service.SupplyChainValidationService; import lombok.extern.log4j.Log4j2; import org.apache.commons.lang3.ArrayUtils; import org.bouncycastle.asn1.x500.X500Name; @@ -39,7 +34,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; -import org.springframework.data.domain.Sort; import org.springframework.stereotype.Service; import java.io.IOException; @@ -57,12 +51,11 @@ @Log4j2 public class CertificateRequestProcessor { private final SupplyChainValidationService supplyChainValidationService; - private final CertificateRepository certificateRepository; + private final CredentialManagementService credentialManagementService; private final DeviceRepository deviceRepository; - private final X509Certificate acaCertificate; private final TPM2ProvisionerStateRepository tpm2ProvisionerStateRepository; private final PolicyRepository policyRepository; - private final PublicKeyAlgorithm publicKeyAlgorithm; + private final X509Certificate acaCertificate; private final int certificateValidityInDays; private final PrivateKey privateKey; @@ -70,31 +63,29 @@ public class CertificateRequestProcessor { * Constructor. * * @param supplyChainValidationService object that is used to run provisioning - * @param certificateRepository db connector for all certificates. + * @param credentialManagementService credential management service * @param deviceRepository database connector for Devices. + * @param tpm2ProvisionerStateRepository db connector for provisioner state. * @param privateKey private key used for communication authentication * @param acaCertificate object used to create credential * @param certificateValidityInDays int for the time in which a certificate is valid. - * @param tpm2ProvisionerStateRepository db connector for provisioner state. * @param policyRepository db connector for policies. */ @Autowired public CertificateRequestProcessor(final SupplyChainValidationService supplyChainValidationService, - final CertificateRepository certificateRepository, + final CredentialManagementService credentialManagementService, final DeviceRepository deviceRepository, + final TPM2ProvisionerStateRepository tpm2ProvisionerStateRepository, final PrivateKey privateKey, @Qualifier("leafACACert") final X509Certificate acaCertificate, - @Value("${aca.current.public.key.algorithm}") final String publicKeyAlgorithmStr, @Value("${aca.certificates.validity}") final int certificateValidityInDays, - final TPM2ProvisionerStateRepository tpm2ProvisionerStateRepository, final PolicyRepository policyRepository) { + this.credentialManagementService = credentialManagementService; this.certificateValidityInDays = certificateValidityInDays; this.supplyChainValidationService = supplyChainValidationService; - this.certificateRepository = certificateRepository; this.deviceRepository = deviceRepository; this.acaCertificate = acaCertificate; this.tpm2ProvisionerStateRepository = tpm2ProvisionerStateRepository; - this.publicKeyAlgorithm = PublicKeyAlgorithm.fromString(publicKeyAlgorithmStr); this.policyRepository = policyRepository; this.privateKey = privateKey; } @@ -154,25 +145,25 @@ public byte[] processCertificateRequest(final byte[] certificateRequest) { ProvisionerTpm2.IdentityClaim claim = ProvisionUtils.parseIdentityClaim(identityClaim); // Get endorsement public key - PublicKey ekPub = ProvisionUtils.parsePublicKeyFromPublicDataSegment(publicKeyAlgorithm, + PublicKey ekPublicKey = ProvisionUtils.parsePublicKeyFromPublicDataSegment( claim.getEkPublicArea().toByteArray()); // Get attestation public key - PublicKey akPub = ProvisionUtils.parsePublicKeyFromPublicDataSegment(publicKeyAlgorithm, + PublicKey akPublicKey = ProvisionUtils.parsePublicKeyFromPublicDataSegment( claim.getAkPublicArea().toByteArray()); // Get Endorsement Credential if it exists or was uploaded EndorsementCredential endorsementCredential = - CredentialManagementHelper.parseEcFromIdentityClaim(claim, ekPub, certificateRepository); + credentialManagementService.parseEcFromIdentityClaim(claim, ekPublicKey); // Get Platform Credentials if they exist or were uploaded - List platformCredentials = CredentialManagementHelper.parsePcsFromIdentityClaim(claim, - endorsementCredential, certificateRepository); + List platformCredentials = credentialManagementService.parsePcsFromIdentityClaim(claim, + endorsementCredential); // Get LDevID public key if it exists - PublicKey ldevidPub = null; + PublicKey ldevidPublicKey = null; if (claim.hasLdevidPublicArea()) { - ldevidPub = ProvisionUtils.parsePublicKeyFromPublicDataSegment(publicKeyAlgorithm, + ldevidPublicKey = ProvisionUtils.parsePublicKeyFromPublicDataSegment( claim.getLdevidPublicArea().toByteArray()); } @@ -209,11 +200,12 @@ public byte[] processCertificateRequest(final byte[] certificateRequest) { AppraisalStatus.Status validationResult = doQuoteValidation(device); if (validationResult == AppraisalStatus.Status.PASS) { // Create signed, attestation certificate - X509Certificate attestationCertificate = generateCredential(akPub, + X509Certificate attestationCertificate = generateCredential(akPublicKey, endorsementCredential, platformCredentials, deviceName, acaCertificate); - if (ldevidPub != null) { + + if (ldevidPublicKey != null) { // Create signed LDevID certificate - X509Certificate ldevidCertificate = generateCredential(ldevidPub, + X509Certificate ldevidCertificate = generateCredential(ldevidPublicKey, endorsementCredential, platformCredentials, deviceName, acaCertificate); byte[] derEncodedAttestationCertificate = ProvisionUtils.getDerEncodedCertificate( attestationCertificate); @@ -226,26 +218,29 @@ public byte[] processCertificateRequest(final byte[] certificateRequest) { // We validated the nonce and made use of the identity claim so state can be deleted tpm2ProvisionerStateRepository.delete(tpm2ProvisionerState); - boolean generateAtt = saveAttestationCertificate(certificateRepository, - derEncodedAttestationCertificate, - endorsementCredential, platformCredentials, device, false); + + boolean generateAtt = + credentialManagementService.saveAttestationCertificate(derEncodedAttestationCertificate, + endorsementCredential, platformCredentials, device, false); + boolean generateLDevID = - saveAttestationCertificate(certificateRepository, derEncodedLdevidCertificate, + credentialManagementService.saveAttestationCertificate(derEncodedLdevidCertificate, endorsementCredential, platformCredentials, device, true); ProvisionerTpm2.CertificateResponse.Builder certificateResponseBuilder = ProvisionerTpm2.CertificateResponse. newBuilder().setStatus(ProvisionerTpm2.ResponseStatus.PASS); + if (generateAtt) { certificateResponseBuilder = certificateResponseBuilder.setCertificate(pemEncodedAttestationCertificate); } + if (generateLDevID) { certificateResponseBuilder = certificateResponseBuilder.setLdevidCertificate(pemEncodedLdevidCertificate); } - ProvisionerTpm2.CertificateResponse certificateResponse = - certificateResponseBuilder.build(); + ProvisionerTpm2.CertificateResponse certificateResponse = certificateResponseBuilder.build(); String certResponseJsonStringAfterSuccess = ""; try { @@ -291,9 +286,10 @@ public byte[] processCertificateRequest(final byte[] certificateRequest) { ProvisionerTpm2.CertificateResponse. newBuilder().setStatus(ProvisionerTpm2.ResponseStatus.PASS); - boolean generateAtt = saveAttestationCertificate(certificateRepository, + boolean generateAtt = credentialManagementService.saveAttestationCertificate( derEncodedAttestationCertificate, endorsementCredential, platformCredentials, device, false); + if (generateAtt) { certificateResponseBuilder = certificateResponseBuilder.setCertificate(pemEncodedAttestationCertificate); @@ -377,8 +373,7 @@ public byte[] processCertificateRequest(final byte[] certificateRequest) { log.info("------------- Start Of Protobuf Log Of Certificate Request After Failed " + "Validation (Invalid Nonce) -------------"); - log.error("Could not process credential request." - + " Invalid nonce provided: {}", + log.error("Could not process credential request. Invalid nonce provided: {}", certificateRequestJsonString.isEmpty() ? request : certificateRequestJsonString); log.info("------------- End Of Protobuf Log Of Certificate Request After Failed " @@ -512,70 +507,4 @@ private X509Certificate generateCredential(final PublicKey publicKey, + "identity credential: " + exception.getMessage(), exception); } } - - /** - * 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 - * @param platformCredentials the platform credentials used to generate the AC - * @param device the device to which the attestation certificate is tied - * @param ldevID whether the certificate is a ldevid - * @return whether the certificate was saved successfully - */ - public boolean saveAttestationCertificate(final CertificateRepository certificateRepository, - final byte[] derEncodedAttestationCertificate, - final EndorsementCredential endorsementCredential, - final List platformCredentials, - final Device device, - final boolean ldevID) { - List issuedAc; - boolean generateCertificate = true; - PolicySettings policySettings; - Date currentDate = new Date(); - int days; - try { - // save issued certificate - IssuedAttestationCertificate attCert = new IssuedAttestationCertificate( - derEncodedAttestationCertificate, endorsementCredential, platformCredentials, ldevID); - - policySettings = policyRepository.findByName("Default"); - - Sort sortCriteria = Sort.by(Sort.Direction.DESC, "endValidity"); - issuedAc = certificateRepository.findByDeviceIdAndLdevID(device.getId(), ldevID, - sortCriteria); - - generateCertificate = ldevID ? policySettings.isIssueDevIdCertificateEnabled() - : policySettings.isIssueAttestationCertificateEnabled(); - - if (issuedAc != null && !issuedAc.isEmpty() - && (ldevID ? policySettings.isGenerateDevIdCertificateOnExpiration() - : policySettings.isGenerateAttestationCertificateOnExpiration())) { - if (issuedAc.getFirst().getEndValidity().after(currentDate)) { - // so the issued AC is not expired - // however are we within the threshold - days = ProvisionUtils.daysBetween(currentDate, issuedAc.getFirst().getEndValidity()); - generateCertificate = - days < (ldevID ? policySettings.getDevIdReissueThreshold() - : policySettings.getReissueThreshold()); - } - } - - if (generateCertificate) { - attCert.setDeviceId(device.getId()); - attCert.setDeviceName(device.getName()); - certificateRepository.save(attCert); - } - } catch (Exception e) { - log.error("Error saving generated Attestation Certificate to database.", e); - throw new CertificateProcessingException( - "Encountered error while storing Attestation Certificate: " - + e.getMessage(), e); - } - - return generateCertificate; - } } diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/helper/CredentialManagementHelper.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/service/CredentialManagementService.java similarity index 61% rename from HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/helper/CredentialManagementHelper.java rename to HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/service/CredentialManagementService.java index 98c4369be..f983bc894 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/helper/CredentialManagementHelper.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/service/CredentialManagementService.java @@ -1,46 +1,130 @@ -package hirs.attestationca.persist.provision.helper; +package hirs.attestationca.persist.provision.service; import com.google.protobuf.ByteString; import hirs.attestationca.configuration.provisionerTpm2.ProvisionerTpm2; import hirs.attestationca.persist.DBManagerException; import hirs.attestationca.persist.entity.manager.CertificateRepository; +import hirs.attestationca.persist.entity.manager.PolicyRepository; 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.certificate.EndorsementCredential; +import hirs.attestationca.persist.entity.userdefined.certificate.IssuedAttestationCertificate; import hirs.attestationca.persist.entity.userdefined.certificate.PlatformCredential; +import hirs.attestationca.persist.exceptions.CertificateProcessingException; +import hirs.attestationca.persist.provision.helper.ProvisionUtils; import lombok.extern.log4j.Log4j2; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Sort; +import org.springframework.stereotype.Service; import java.io.IOException; import java.security.PublicKey; +import java.util.Date; import java.util.LinkedList; import java.util.List; /** - * Utility class which includes credential management functions used by the ACA. + * Service layer class that encapsulates credential management functions used by the ACA. */ +@Service @Log4j2 -public final class CredentialManagementHelper { +public class CredentialManagementService { + private final PolicyRepository policyRepository; + private final CertificateRepository certificateRepository; - private CredentialManagementHelper() { + /** + * Constructor. + * + * @param policyRepository policy repository + * @param certificateRepository certificate repository + */ + @Autowired + private CredentialManagementService(final PolicyRepository policyRepository, + final CertificateRepository certificateRepository) { + this.policyRepository = policyRepository; + this.certificateRepository = certificateRepository; + } + + /** + * Parses an Endorsement Credential from a Protobuf generated + * IdentityClaim. Will also check if the Endorsement Credential was already uploaded. + * Persists the Endorsement Credential if it does not already exist. + * + * @param identityClaim a Protobuf generated Identity Claim object + * @param ekPublicKey the endorsement public key from the Identity Claim object + * @return the Endorsement Credential, if one exists, null otherwise + */ + public EndorsementCredential parseEcFromIdentityClaim(final ProvisionerTpm2.IdentityClaim identityClaim, + final PublicKey ekPublicKey) { + EndorsementCredential endorsementCredential = null; + + if (identityClaim.hasEndorsementCredential()) { + endorsementCredential = storeEndorsementCredential(identityClaim.getEndorsementCredential().toByteArray(), + identityClaim.getDv().getNw().getHostname()); + } else if (ekPublicKey != null) { + log.warn("Endorsement Credential was not in the identity claim from the client.Checking for uploads."); + endorsementCredential = getEndorsementCredential(ekPublicKey); + } else { + log.warn("No endorsement credential was received in identity claim and no EK Public" + + " Key was provided to check for uploaded certificates."); + } + + return endorsementCredential; + } + + /** + * Helper method to parse a set of Platform Credentials from a Protobuf generated + * IdentityClaim and Endorsement Credential. Persists the Platform Credentials if they + * do not already exist. + * + * @param identityClaim a Protobuf generated Identity Claim object + * @param endorsementCredential an endorsement credential to check if platform credentials + * exist + * @return the List of Platform Credentials, if they exist, an empty set otherwise + */ + public List parsePcsFromIdentityClaim(final ProvisionerTpm2.IdentityClaim identityClaim, + final EndorsementCredential endorsementCredential) { + List platformCredentials = new LinkedList<>(); + + if (identityClaim.getPlatformCredentialCount() > 0) { + + List platformCredentialList = identityClaim.getPlatformCredentialList(); + + for (ByteString platformCredential : platformCredentialList) { + if (!platformCredential.isEmpty()) { + PlatformCredential storedPlatformCredential = + storePlatformCredential(platformCredential.toByteArray(), + identityClaim.getDv().getNw().getHostname()); + + if (storedPlatformCredential != null) { + platformCredentials.add(storedPlatformCredential); + } + } + } + } else if (endorsementCredential != null) { + // if none in the identity claim, look for uploaded platform credentials + log.warn("PC was not in the identity claim from the client. Checking for uploads."); + platformCredentials.addAll(getPlatformCredentials(endorsementCredential)); + } else { + log.warn("No platform credential received in identity claim."); + } + + return platformCredentials; } /** * Parses and stores the EK in the cert manager. If the cert is already present and archived, * it is unarchived. * - * @param certificateRepository the certificate manager used for storage - * @param endorsementBytes the raw EK bytes used for parsing - * @param deviceName the host name + * @param endorsementBytes the raw EK bytes used for parsing + * @param deviceName the host name * @return the parsed, valid EK * @throws IllegalArgumentException if the provided bytes are not a valid EK. */ - public static EndorsementCredential storeEndorsementCredential( - final CertificateRepository certificateRepository, - final byte[] endorsementBytes, final String deviceName) throws IllegalArgumentException { - - if (certificateRepository == null) { - throw new IllegalArgumentException("null certificate manager"); - } + public EndorsementCredential storeEndorsementCredential(final byte[] endorsementBytes, final String deviceName) + throws IllegalArgumentException { if (endorsementBytes == null) { throw new IllegalArgumentException("null endorsement credential bytes"); @@ -86,19 +170,11 @@ public static EndorsementCredential storeEndorsementCredential( * Parses and stores the PC in the cert manager. If the cert is already present and archived, * it is unarchived. * - * @param certificateRepository the certificate manager used for storage - * @param platformBytes the raw PC bytes used for parsing - * @param deviceName the host name of the associated machine + * @param platformBytes the raw PC bytes used for parsing + * @param deviceName the host name of the associated machine * @return the parsed, valid PC, or null if the provided bytes are not a valid EK. */ - public static PlatformCredential storePlatformCredential( - final CertificateRepository certificateRepository, - final byte[] platformBytes, final String deviceName) { - - if (certificateRepository == null) { - log.error("The provided certificate repository is null."); - throw new IllegalArgumentException("null certificate manager"); - } + public PlatformCredential storePlatformCredential(final byte[] platformBytes, final String deviceName) { if (platformBytes == null) { log.error("The provided platform credential byte array is null."); @@ -170,91 +246,76 @@ public static PlatformCredential storePlatformCredential( } /** - * Helper method to parse an Endorsement Credential from a Protobuf generated - * IdentityClaim. Will also check if the Endorsement Credential was already uploaded. - * Persists the Endorsement Credential if it does not already exist. + * Creates an {@link IssuedAttestationCertificate} object and set its corresponding device and persists it. * - * @param identityClaim a Protobuf generated Identity Claim object - * @param ekPub the endorsement public key from the Identity Claim object - * @param certificateRepository db connector from certificates - * @return the Endorsement Credential, if one exists, null otherwise + * @param derEncodedAttestationCertificate the byte array representing the Attestation + * certificate + * @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 ldevID whether the certificate is a ldevid + * @return whether the certificate was saved successfully */ - public static EndorsementCredential parseEcFromIdentityClaim( - final ProvisionerTpm2.IdentityClaim identityClaim, - final PublicKey ekPub, final CertificateRepository certificateRepository) { - EndorsementCredential endorsementCredential = null; - - if (identityClaim.hasEndorsementCredential()) { - endorsementCredential = CredentialManagementHelper.storeEndorsementCredential( - certificateRepository, - identityClaim.getEndorsementCredential().toByteArray(), - identityClaim.getDv().getNw().getHostname()); - } else if (ekPub != null) { - log.warn("Endorsement Cred was not in the identity claim from the client." - + " Checking for uploads."); - endorsementCredential = getEndorsementCredential(ekPub, certificateRepository); - } else { - log.warn("No endorsement credential was received in identity claim and no EK Public" - + " Key was provided to check for uploaded certificates."); - } - - return endorsementCredential; - } - - /** - * Helper method to parse a set of Platform Credentials from a Protobuf generated - * IdentityClaim and Endorsement Credential. Persists the Platform Credentials if they - * do not already exist. - * - * @param identityClaim a Protobuf generated Identity Claim object - * @param endorsementCredential an endorsement credential to check if platform credentials - * exist - * @param certificateRepository db connector from certificates - * @return the List of Platform Credentials, if they exist, an empty set otherwise - */ - public static List parsePcsFromIdentityClaim( - final ProvisionerTpm2.IdentityClaim identityClaim, + public boolean saveAttestationCertificate( + final byte[] derEncodedAttestationCertificate, final EndorsementCredential endorsementCredential, - final CertificateRepository certificateRepository) { - List platformCredentials = new LinkedList<>(); - - if (identityClaim.getPlatformCredentialCount() > 0) { - - List platformCredentialList = identityClaim.getPlatformCredentialList(); - - for (ByteString platformCredential : platformCredentialList) { - if (!platformCredential.isEmpty()) { - PlatformCredential storedPlatformCredential = - CredentialManagementHelper.storePlatformCredential( - certificateRepository, platformCredential.toByteArray(), - identityClaim.getDv().getNw().getHostname()); - - if (storedPlatformCredential != null) { - platformCredentials.add(storedPlatformCredential); - } + final List platformCredentials, + final Device device, + final boolean ldevID) { + List issuedAc; + boolean generateCertificate = true; + PolicySettings policySettings; + Date currentDate = new Date(); + int days; + try { + // save issued certificate + IssuedAttestationCertificate attCert = new IssuedAttestationCertificate( + derEncodedAttestationCertificate, endorsementCredential, platformCredentials, ldevID); + + policySettings = policyRepository.findByName("Default"); + + Sort sortCriteria = Sort.by(Sort.Direction.DESC, "endValidity"); + issuedAc = certificateRepository.findByDeviceIdAndLdevID(device.getId(), ldevID, + sortCriteria); + + generateCertificate = ldevID ? policySettings.isIssueDevIdCertificateEnabled() + : policySettings.isIssueAttestationCertificateEnabled(); + + if (issuedAc != null && !issuedAc.isEmpty() + && (ldevID ? policySettings.isGenerateDevIdCertificateOnExpiration() + : policySettings.isGenerateAttestationCertificateOnExpiration())) { + if (issuedAc.getFirst().getEndValidity().after(currentDate)) { + // so the issued AC is not expired + // however are we within the threshold + days = ProvisionUtils.daysBetween(currentDate, issuedAc.getFirst().getEndValidity()); + generateCertificate = + days < (ldevID ? policySettings.getDevIdReissueThreshold() + : policySettings.getReissueThreshold()); } } - } else if (endorsementCredential != null) { - // if none in the identity claim, look for uploaded platform credentials - log.warn("PC was not in the identity claim from the client. Checking for uploads."); - platformCredentials.addAll(getPlatformCredentials(certificateRepository, endorsementCredential)); - } else { - log.warn("No platform credential received in identity claim."); + + if (generateCertificate) { + attCert.setDeviceId(device.getId()); + attCert.setDeviceName(device.getName()); + certificateRepository.save(attCert); + } + } catch (Exception e) { + log.error("Error saving generated Attestation Certificate to database.", e); + throw new CertificateProcessingException( + "Encountered error while storing Attestation Certificate: " + + e.getMessage(), e); } - return platformCredentials; + return generateCertificate; } /** * 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 + * @param ekPublicKey the EK public key * @return the Endorsement credential, if found, otherwise null */ - private static EndorsementCredential getEndorsementCredential( - final PublicKey ekPublicKey, - final CertificateRepository certificateRepository) { + private EndorsementCredential getEndorsementCredential(final PublicKey ekPublicKey) { log.debug("Searching for endorsement credential based on public key: {}", ekPublicKey); if (ekPublicKey == null) { @@ -282,12 +343,10 @@ private static EndorsementCredential getEndorsementCredential( /** * Helper method that retrieves all the platform credentials associated with the provided Endorsement Credential. * - * @param certificateRepository certificateRepository - * @param ec endorsement credential + * @param ec endorsement credential * @return list of platform credentials */ - private static List getPlatformCredentials(final CertificateRepository certificateRepository, - final EndorsementCredential ec) { + private List getPlatformCredentials(final EndorsementCredential ec) { List credentials = null; if (ec == null) { diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/service/DeviceManagementService.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/service/DeviceManagementService.java new file mode 100644 index 000000000..ded26b257 --- /dev/null +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/service/DeviceManagementService.java @@ -0,0 +1,579 @@ +package hirs.attestationca.persist.provision.service; + +import com.google.protobuf.ByteString; +import hirs.attestationca.configuration.provisionerTpm2.ProvisionerTpm2; +import hirs.attestationca.persist.entity.manager.DeviceRepository; +import hirs.attestationca.persist.entity.manager.ReferenceDigestValueRepository; +import hirs.attestationca.persist.entity.manager.ReferenceManifestRepository; +import hirs.attestationca.persist.entity.userdefined.Device; +import hirs.attestationca.persist.entity.userdefined.ReferenceManifest; +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.rim.BaseReferenceManifest; +import hirs.attestationca.persist.entity.userdefined.rim.EventLogMeasurements; +import hirs.attestationca.persist.entity.userdefined.rim.ReferenceDigestValue; +import hirs.attestationca.persist.entity.userdefined.rim.SupportReferenceManifest; +import hirs.attestationca.persist.exceptions.IdentityProcessingException; +import hirs.utils.HexUtils; +import hirs.utils.SwidResource; +import hirs.utils.enums.DeviceInfoEnums; +import hirs.utils.tpm.eventlog.TCGEventLog; +import hirs.utils.tpm.eventlog.TpmPcrEvent; +import jakarta.xml.bind.UnmarshalException; +import lombok.extern.log4j.Log4j2; +import org.apache.commons.codec.binary.Hex; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateException; +import java.util.ArrayList; +import java.util.Base64; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.UUID; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +@Service +@Log4j2 +public class DeviceManagementService { + private static final int NUM_OF_VARIABLES = 5; + private static final int MAC_BYTES = 6; + + private final DeviceRepository deviceRepository; + private final ReferenceManifestRepository referenceManifestRepository; + private final ReferenceDigestValueRepository referenceDigestValueRepository; + + @Autowired + public DeviceManagementService(final DeviceRepository deviceRepository, + final ReferenceManifestRepository referenceManifestRepository, + final ReferenceDigestValueRepository referenceDigestValueRepository) { + this.deviceRepository = deviceRepository; + this.referenceManifestRepository = referenceManifestRepository; + this.referenceDigestValueRepository = referenceDigestValueRepository; + } + + /** + * Utilizes the identity claim to produce a device info report. + * + * @param claim identity claim + * @return device info + */ + public Device processDeviceInfo(final ProvisionerTpm2.IdentityClaim claim) { + DeviceInfoReport deviceInfoReport = null; + + try { + deviceInfoReport = parseDeviceInfo(claim); + } catch (NoSuchAlgorithmException noSaEx) { + log.error(noSaEx); + } + + if (deviceInfoReport == null) { + log.error("Failed to deserialize Device Info Report"); + throw new IdentityProcessingException("Device Info Report failed to deserialize " + + "from Identity Claim"); + } + + log.info("Processing Device Info Report"); + + // store device and device info report. + 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); + } + device.setDeviceInfo(deviceInfoReport); + return this.deviceRepository.save(device); + } + + /** + * Helper method that creates a Device Info Report objec using the provided protobuf identity claim's device info. + * + * @param protoIdentityClaim the protobuf serialized identity claim containing the device info + * @return {@link DeviceInfoReport} + */ + private DeviceInfoReport parseDeviceInfo(final ProvisionerTpm2.IdentityClaim protoIdentityClaim) + throws NoSuchAlgorithmException { + ProvisionerTpm2.DeviceInfo deviceInfoProto = protoIdentityClaim.getDv(); + + String pcrValues = ""; + + if (deviceInfoProto.hasPcrslist()) { + pcrValues = deviceInfoProto.getPcrslist().toStringUtf8(); + } + + // Get Hardware info + HardwareInfo hardwareInfo = getHardwareInfo(deviceInfoProto.getHw()); + + retrieveDeviceInfoFromRIMs(deviceInfoProto, hardwareInfo); + + // Get TPM info, currently unimplemented + TPMInfo tpmInfo = new TPMInfo(DeviceInfoEnums.NOT_SPECIFIED, + (short) 0, + (short) 0, + (short) 0, + (short) 0, + pcrValues.getBytes(StandardCharsets.UTF_8), + null, null); + + // Get Network info + NetworkInfo networkInfo = getNetworkInfo(deviceInfoProto.getNw()); + + // Get Firmware info + FirmwareInfo firmwareInfo = getFirmwareInfo(deviceInfoProto.getFw()); + + // Get OS info + OSInfo osInfo = getOSInfo(deviceInfoProto.getOs()); + + // Create final report + DeviceInfoReport dvReport = new DeviceInfoReport(networkInfo, osInfo, firmwareInfo, hardwareInfo, tpmInfo, + protoIdentityClaim.getClientVersion()); + dvReport.setPaccorOutputString(protoIdentityClaim.getPaccorOutput()); + + return dvReport; + } + + /** + * Helper method that creates a hardware info object using the provided protobuf's version of hardware info. + * + * @param hardwareInfoProto Protobuf's version of Hardware Info + * @return {@link HardwareInfo} + */ + private HardwareInfo getHardwareInfo(ProvisionerTpm2.HardwareInfo hardwareInfoProto) { + + // Make sure chassis info has at least one chassis + String firstChassisSerialNumber = DeviceInfoEnums.NOT_SPECIFIED; + if (hardwareInfoProto.getChassisInfoCount() > 0) { + firstChassisSerialNumber = hardwareInfoProto.getChassisInfo(0).getSerialNumber(); + } + + // Make sure baseboard info has at least one baseboard + String firstBaseboardSerialNumber = DeviceInfoEnums.NOT_SPECIFIED; + if (hardwareInfoProto.getBaseboardInfoCount() > 0) { + firstBaseboardSerialNumber = hardwareInfoProto.getBaseboardInfo(0).getSerialNumber(); + } + + return new HardwareInfo(hardwareInfoProto.getManufacturer(), hardwareInfoProto.getProductName(), + hardwareInfoProto.getProductVersion(), hardwareInfoProto.getSystemSerialNumber(), + firstChassisSerialNumber, firstBaseboardSerialNumber); + } + + /** + * @param networkInfoProto Protobuf's version of Network Info + * @return {@link NetworkInfo} + */ + private NetworkInfo getNetworkInfo(ProvisionerTpm2.NetworkInfo networkInfoProto) { + InetAddress ip = null; + try { + ip = InetAddress.getByName(networkInfoProto.getIpAddress()); + } catch (UnknownHostException uhEx) { + log.error("Unable to parse IP address: ", uhEx); + } + String[] macAddressParts = networkInfoProto.getMacAddress().split(":"); + + // convert mac hex string to byte values + byte[] macAddressBytes = new byte[MAC_BYTES]; + Integer hex; + if (macAddressParts.length == MAC_BYTES) { + for (int i = 0; i < MAC_BYTES; i++) { + hex = HexUtils.hexToInt(macAddressParts[i]); + macAddressBytes[i] = hex.byteValue(); + } + } + + return new NetworkInfo(networkInfoProto.getHostname(), ip, macAddressBytes); + } + + /** + * @param osInfoProto Protobuf's version of OS Info + * @return {@link OSInfo} + */ + private OSInfo getOSInfo(ProvisionerTpm2.OsInfo osInfoProto) { + return new OSInfo(osInfoProto.getOsName(), osInfoProto.getOsVersion(), osInfoProto.getOsArch(), + osInfoProto.getDistribution(), osInfoProto.getDistributionRelease()); + } + + /** + * @param firmwareInfoProto Protobuf's version of Firmware Info + * @return {@link FirmwareInfo} + */ + private FirmwareInfo getFirmwareInfo(ProvisionerTpm2.FirmwareInfo firmwareInfoProto) { + return new FirmwareInfo(firmwareInfoProto.getBiosVendor(), firmwareInfoProto.getBiosVersion(), + firmwareInfoProto.getBiosReleaseDate()); + } + + /** + * @param deviceInfoProto + * @param hw + * @throws NoSuchAlgorithmException + */ + private void retrieveDeviceInfoFromRIMs(ProvisionerTpm2.DeviceInfo deviceInfoProto, HardwareInfo hw) + throws NoSuchAlgorithmException { + + // check for RIM Base and Support files, if they don't exist in the database, load them + final String defaultClientName = String.format("%s_%s", deviceInfoProto.getHw().getManufacturer(), + deviceInfoProto.getHw().getProductName()); + + BaseReferenceManifest baseRim = null; + SupportReferenceManifest supportRim = null; + EventLogMeasurements integrityMeasurements; + boolean isReplacement = false; + String replacementRimId = ""; + String tagId = ""; + String fileName = ""; + Pattern pattern = Pattern.compile("(\\S+(\\.(?i)(rimpcr|rimel|bin|log))$)"); + Matcher matcher; + MessageDigest messageDigest = MessageDigest.getInstance("SHA-256"); + + if (deviceInfoProto.getSwidfileCount() > 0) { + for (ByteString swidFile : deviceInfoProto.getSwidfileList()) { + try { + baseRim = (BaseReferenceManifest) referenceManifestRepository.findByBase64Hash(Base64.getEncoder() + .encodeToString(messageDigest.digest(swidFile.toByteArray()))); + + if (baseRim == null) { + /* + Either the swidFile does not have a corresponding base RIM in the backend + or it was deleted. Check if there is a replacement by comparing tagId against + all other base RIMs, and then set the corresponding support rim's deviceName. + */ + baseRim = new BaseReferenceManifest(String.format("%s.swidtag", defaultClientName), + swidFile.toByteArray()); + + List baseRims = referenceManifestRepository.findAllBaseRims(); + + for (BaseReferenceManifest bRim : baseRims) { + if (bRim.getTagId().equals(baseRim.getTagId())) { + baseRim = bRim; + replacementRimId = baseRim.getAssociatedRim().toString(); + isReplacement = true; + break; + } + } + baseRim.setDeviceName(deviceInfoProto.getNw().getHostname()); + this.referenceManifestRepository.save(baseRim); + } else if (baseRim.isArchived()) { + /* + This block accounts for RIMs that may have been soft-deleted (archived) + in an older version of the ACA. + */ + List rims = referenceManifestRepository.findByArchiveFlag(false); + for (ReferenceManifest rim : rims) { + if (rim.isBase() && rim.getTagId().equals(baseRim.getTagId()) + && rim.getCreateTime().after(baseRim.getCreateTime())) { + baseRim.setDeviceName(null); + baseRim = (BaseReferenceManifest) rim; + baseRim.setDeviceName(deviceInfoProto.getNw().getHostname()); + } + } + if (baseRim.isArchived()) { + throw new Exception("Unable to locate an unarchived base RIM."); + } else { + this.referenceManifestRepository.save(baseRim); + } + } else { + baseRim.setDeviceName(deviceInfoProto.getNw().getHostname()); + this.referenceManifestRepository.save(baseRim); + } + tagId = baseRim.getTagId(); + } catch (UnmarshalException e) { + log.error(e); + } catch (Exception ex) { + log.error("Failed to load base rim: {}", ex.getMessage()); + } + } + } else { + log.warn("{} did not send swid tag file...", deviceInfoProto.getNw().getHostname()); + } + + if (deviceInfoProto.getLogfileCount() > 0) { + for (ByteString logFile : deviceInfoProto.getLogfileList()) { + try { + supportRim = + (SupportReferenceManifest) referenceManifestRepository.findByHexDecHashAndRimType( + Hex.encodeHexString(messageDigest.digest(logFile.toByteArray())), + ReferenceManifest.SUPPORT_RIM); + if (supportRim == null) { + /* + Either the logFile does not have a corresponding support RIM in the backend + or it was deleted. The support RIM for a replacement base RIM is handled + in the previous loop block. + */ + if (isReplacement) { + Optional replacementRim = + referenceManifestRepository.findById(UUID.fromString(replacementRimId)); + if (replacementRim.isPresent()) { + supportRim = (SupportReferenceManifest) replacementRim.get(); + supportRim.setDeviceName(deviceInfoProto.getNw().getHostname()); + } else { + throw new Exception("Unable to locate support RIM " + replacementRimId); + } + } else { + supportRim = new SupportReferenceManifest(String.format("%s.rimel", defaultClientName), + logFile.toByteArray()); + // this is a validity check + new TCGEventLog(supportRim.getRimBytes()); + // no issues, continue + supportRim.setPlatformManufacturer(deviceInfoProto.getHw().getManufacturer()); + supportRim.setPlatformModel(deviceInfoProto.getHw().getProductName()); + supportRim.setFileName(String.format("%s_[%s].rimel", defaultClientName, + supportRim.getHexDecHash().substring( + supportRim.getHexDecHash().length() - NUM_OF_VARIABLES))); + } + supportRim.setDeviceName(deviceInfoProto.getNw().getHostname()); + this.referenceManifestRepository.save(supportRim); + } else if (supportRim.isArchived()) { + /* + This block accounts for RIMs that may have been soft-deleted (archived) + in an older version of the ACA. + */ + List rims = referenceManifestRepository.findByArchiveFlag(false); + for (ReferenceManifest rim : rims) { + if (rim.isSupport() + && rim.getTagId().equals(supportRim.getTagId()) + && rim.getCreateTime().after(supportRim.getCreateTime())) { + supportRim.setDeviceName(null); + supportRim = (SupportReferenceManifest) rim; + supportRim.setDeviceName(deviceInfoProto.getNw().getHostname()); + } + } + if (supportRim.isArchived()) { + throw new Exception("Unable to locate an unarchived support RIM."); + } else { + this.referenceManifestRepository.save(supportRim); + } + } else { + supportRim.setDeviceName(deviceInfoProto.getNw().getHostname()); + this.referenceManifestRepository.save(supportRim); + } + } catch (IOException ioEx) { + log.error(ioEx); + } catch (Exception ex) { + log.error("Failed to load support rim: {}", ex.getMessage()); + } + } + } else { + log.warn("{} did not send support RIM file...", deviceInfoProto.getNw().getHostname()); + } + + //update Support RIMs and Base RIMs. + for (ByteString swidFile : deviceInfoProto.getSwidfileList()) { + baseRim = (BaseReferenceManifest) referenceManifestRepository + .findByBase64Hash(Base64.getEncoder().encodeToString(messageDigest.digest( + swidFile.toByteArray()))); + if (baseRim != null) { + // get file name to use + for (SwidResource swid : baseRim.getFileResources()) { + matcher = pattern.matcher(swid.getName()); + if (matcher.matches()) { + //found the file name + int dotIndex = swid.getName().lastIndexOf("."); + fileName = swid.getName().substring(0, dotIndex); + baseRim.setFileName(String.format("%s.swidtag", + fileName)); + } + + // now update support rim + SupportReferenceManifest dbSupport = + (SupportReferenceManifest) referenceManifestRepository + .findByHexDecHashAndRimType(swid.getHashValue(), + ReferenceManifest.SUPPORT_RIM); + if (dbSupport != null) { + dbSupport.setFileName(swid.getName()); + dbSupport.setSwidTagVersion(baseRim.getSwidTagVersion()); + dbSupport.setTagId(baseRim.getTagId()); + dbSupport.setSwidTagVersion(baseRim.getSwidTagVersion()); + dbSupport.setSwidVersion(baseRim.getSwidVersion()); + dbSupport.setSwidPatch(baseRim.isSwidPatch()); + dbSupport.setSwidSupplemental(baseRim.isSwidSupplemental()); + baseRim.setAssociatedRim(dbSupport.getId()); + dbSupport.setUpdated(true); + dbSupport.setAssociatedRim(baseRim.getId()); + this.referenceManifestRepository.save(dbSupport); + } else { + log.warn("Could not locate support RIM with hash {}}", swid.getHashValue()); + } + } + this.referenceManifestRepository.save(baseRim); + } + } + + generateDigestRecords(hw.getManufacturer(), hw.getProductName()); + + if (deviceInfoProto.hasLivelog()) { + log.info("Device sent bios measurement log..."); + fileName = String.format("%s.measurement", deviceInfoProto.getNw().getHostname()); + try { + EventLogMeasurements deviceLiveLog = new EventLogMeasurements(fileName, + deviceInfoProto.getLivelog().toByteArray()); + // find previous version. + integrityMeasurements = referenceManifestRepository.byMeasurementDeviceNameUnarchived( + deviceInfoProto.getNw().getHostname()); + + if (integrityMeasurements != null) { + // Find previous log and archive it + integrityMeasurements.archive(); + this.referenceManifestRepository.save(integrityMeasurements); + } + + List baseRims = referenceManifestRepository.getBaseByManufacturerModel( + deviceInfoProto.getHw().getManufacturer(), + deviceInfoProto.getHw().getProductName()); + + integrityMeasurements = deviceLiveLog; + integrityMeasurements.setPlatformManufacturer(deviceInfoProto.getHw().getManufacturer()); + integrityMeasurements.setPlatformModel(deviceInfoProto.getHw().getProductName()); + + if (tagId != null && !tagId.trim().isEmpty()) { + integrityMeasurements.setTagId(tagId); + } + + integrityMeasurements.setDeviceName(deviceInfoProto.getNw().getHostname()); + + this.referenceManifestRepository.save(integrityMeasurements); + + for (BaseReferenceManifest bRim : baseRims) { + if (bRim != null) { + // pull the base versions of the swidtag and rimel and set the + // event log hash for use during provision + SupportReferenceManifest sBaseRim = referenceManifestRepository + .getSupportRimEntityById(bRim.getAssociatedRim()); + if (sBaseRim != null) { + bRim.setEventLogHash(deviceLiveLog.getHexDecHash()); + sBaseRim.setEventLogHash(deviceLiveLog.getHexDecHash()); + referenceManifestRepository.save(bRim); + referenceManifestRepository.save(sBaseRim); + } else { + log.warn("Could not locate support RIM associated with base RIM {}", bRim.getId()); + } + } + } + } catch (IOException ioEx) { + log.error(ioEx); + } + } else { + log.warn("{} did not send bios measurement log...", deviceInfoProto.getNw().getHostname()); + } + } + + /** + * Helper method that generates digest records using the provided device's manufacturer and model + * information. + * + * @param manufacturer device manufacturer + * @param model device model + */ + private void generateDigestRecords(final String manufacturer, final String model) { + List rdValues = new LinkedList<>(); + SupportReferenceManifest baseSupportRim = null; + List supplementalRims = new ArrayList<>(); + List patchRims = new ArrayList<>(); + List dbSupportRims = this.referenceManifestRepository + .getSupportByManufacturerModel(manufacturer, model); + List expectedValues = referenceDigestValueRepository + .findByManufacturerAndModel(manufacturer, model); + + Map digestValueMap = new HashMap<>(); + expectedValues.forEach((rdv) -> digestValueMap.put(rdv.getDigestValue(), rdv)); + + for (SupportReferenceManifest dbSupport : dbSupportRims) { + if (dbSupport.isSwidPatch()) { + patchRims.add(dbSupport); + } else if (dbSupport.isSwidSupplemental()) { + supplementalRims.add(dbSupport); + } else { + // we have a base support rim (verify this is getting set) + baseSupportRim = dbSupport; + } + } + + if (baseSupportRim != null && + referenceDigestValueRepository.findBySupportRimHash(baseSupportRim.getHexDecHash()).isEmpty()) { + try { + TCGEventLog eventLog = new TCGEventLog(baseSupportRim.getRimBytes()); + ReferenceDigestValue rdv; + for (TpmPcrEvent tpe : eventLog.getEventList()) { + rdv = new ReferenceDigestValue(baseSupportRim.getAssociatedRim(), + baseSupportRim.getId(), manufacturer, model, tpe.getPcrIndex(), + tpe.getEventDigestStr(), baseSupportRim.getHexDecHash(), + tpe.getEventTypeStr(), + false, false, true, tpe.getEventContent()); + rdValues.add(rdv); + } + + // since I have the base already I don't have to care about the backward + // linkage + for (SupportReferenceManifest supplemental : supplementalRims) { + eventLog = new TCGEventLog(supplemental.getRimBytes()); + for (TpmPcrEvent tpe : eventLog.getEventList()) { + // all RDVs will have the same base rim + rdv = new ReferenceDigestValue(baseSupportRim.getAssociatedRim(), + supplemental.getId(), manufacturer, model, tpe.getPcrIndex(), + tpe.getEventDigestStr(), baseSupportRim.getHexDecHash(), + tpe.getEventTypeStr(), + false, false, true, tpe.getEventContent()); + rdValues.add(rdv); + } + } + + // Save all supplemental values + ReferenceDigestValue tempRdv; + for (ReferenceDigestValue subRdv : rdValues) { + // check if the value already exists + if (digestValueMap.containsKey(subRdv.getDigestValue())) { + tempRdv = digestValueMap.get(subRdv.getDigestValue()); + if (tempRdv.getPcrIndex() != subRdv.getPcrIndex() + && !tempRdv.getEventType().equals(subRdv.getEventType())) { + referenceDigestValueRepository.save(subRdv); + } else { + // will this be a problem down the line? + referenceDigestValueRepository.save(subRdv); + } + } else { + referenceDigestValueRepository.save(subRdv); + } + digestValueMap.put(subRdv.getDigestValue(), subRdv); + } + + // if a patch value doesn't exist, error? + ReferenceDigestValue dbRdv; + String patchedValue; + for (SupportReferenceManifest patch : patchRims) { + eventLog = new TCGEventLog(patch.getRimBytes()); + for (TpmPcrEvent tpe : eventLog.getEventList()) { + patchedValue = tpe.getEventDigestStr(); + dbRdv = digestValueMap.get(patchedValue); + + if (dbRdv == null) { + log.error("Patching value does not exist ({})", patchedValue); + } else { + // WIP - Until we get patch examples + dbRdv.setPatched(true); + } + } + } + } catch (CertificateException | NoSuchAlgorithmException | IOException ex) { + log.error(ex); + } + } + } +} diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/service/IdentityClaimProcessor.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/service/IdentityClaimProcessor.java new file mode 100644 index 000000000..eeb9ea073 --- /dev/null +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/service/IdentityClaimProcessor.java @@ -0,0 +1,374 @@ +package hirs.attestationca.persist.provision.service; + +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; +import com.google.protobuf.util.JsonFormat; +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.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.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.certificate.attributes.V2.ComponentIdentifierV2; +import hirs.attestationca.persist.entity.userdefined.info.ComponentInfo; +import hirs.attestationca.persist.enums.AppraisalStatus; +import hirs.attestationca.persist.provision.helper.ProvisionUtils; +import hirs.attestationca.persist.validation.SupplyChainCredentialValidator; +import hirs.utils.HexUtils; +import lombok.extern.log4j.Log4j2; +import org.apache.commons.lang3.ArrayUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.io.IOException; +import java.security.PublicKey; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +@Service +@Log4j2 +public class IdentityClaimProcessor { + /** + * Number of bytes to include in the TPM2.0 nonce. + */ + public static final int NONCE_LENGTH = 20; + private static final String PCR_QUOTE_MASK = "0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23"; + + private final SupplyChainValidationService supplyChainValidationService; + private final CredentialManagementService credentialManagementService; + private final DeviceManagementService deviceManagementService; + private final CertificateRepository certificateRepository; + private final ComponentResultRepository componentResultRepository; + private final ComponentInfoRepository componentInfoRepository; + private final DeviceRepository deviceRepository; + private final PolicyRepository policyRepository; + private final TPM2ProvisionerStateRepository tpm2ProvisionerStateRepository; + + /** + * Constructor. + * + * @param supplyChainValidationService supply chain validation service + * @param certificateRepository certificate repository + * @param componentResultRepository component result repository + * @param componentInfoRepository component info repository + * @param deviceRepository device repository + * @param tpm2ProvisionerStateRepository tpm2 provisioner state repository + * @param policyRepository policy repository + */ + @Autowired + public IdentityClaimProcessor( + final SupplyChainValidationService supplyChainValidationService, + final CredentialManagementService credentialManagementService, + final CertificateRepository certificateRepository, + final ComponentResultRepository componentResultRepository, + final ComponentInfoRepository componentInfoRepository, + final DeviceRepository deviceRepository, DeviceManagementService deviceManagementService, + final TPM2ProvisionerStateRepository tpm2ProvisionerStateRepository, + final PolicyRepository policyRepository) { + this.supplyChainValidationService = supplyChainValidationService; + this.credentialManagementService = credentialManagementService; + this.certificateRepository = certificateRepository; + this.componentResultRepository = componentResultRepository; + this.componentInfoRepository = componentInfoRepository; + this.deviceRepository = deviceRepository; + this.deviceManagementService = deviceManagementService; + this.tpm2ProvisionerStateRepository = tpm2ProvisionerStateRepository; + this.policyRepository = policyRepository; + } + + /** + * Basic implementation of the ACA processIdentityClaimTpm2 method. Parses the claim, + * stores the device info, performs supply chain validation, generates a nonce, + * and wraps that nonce with the make credential process before returning it to the client. + * attCert.setPcrValues(pcrValues); + * + * @param identityClaim the request to process, cannot be null + * @return an identity claim response for the specified request containing a wrapped blob + */ + public byte[] processIdentityClaimTpm2(final byte[] identityClaim) { + log.info("Identity Claim has been received and is ready to be processed"); + + if (ArrayUtils.isEmpty(identityClaim)) { + final String errorMsg = "The IdentityClaim sent by the client cannot be null or empty."; + log.error(errorMsg); + throw new IllegalArgumentException(errorMsg); + } + + final PolicySettings policySettings = policyRepository.findByName("Default"); + + // attempt to deserialize Protobuf IdentityClaim + ProvisionerTpm2.IdentityClaim claim = ProvisionUtils.parseIdentityClaim(identityClaim); + + String identityClaimJsonString = ""; + try { + identityClaimJsonString = JsonFormat.printer().print(claim); + } catch (InvalidProtocolBufferException exception) { + log.error("Identity claim could not be parsed properly into a json string"); + } + + // parse the EK Public key from the IdentityClaim once for use in supply chain validation + // and later tpm20MakeCredential function + PublicKey endorsementCredentialPublicKey = + ProvisionUtils.parsePublicKeyFromPublicDataSegment(claim.getEkPublicArea().toByteArray()); + + AppraisalStatus.Status validationResult = AppraisalStatus.Status.FAIL; + + try { + validationResult = doSupplyChainValidation(claim, endorsementCredentialPublicKey); + } catch (Exception ex) { + log.error(ex.getMessage()); + } + + ByteString blobStr = ByteString.copyFrom(new byte[]{}); + + if (validationResult == AppraisalStatus.Status.PASS) { + PublicKey akPub = ProvisionUtils.parsePublicKeyFromPublicDataSegment(claim.getAkPublicArea().toByteArray()); + byte[] nonce = ProvisionUtils.generateRandomBytes(NONCE_LENGTH); + blobStr = ProvisionUtils.tpm20MakeCredential(endorsementCredentialPublicKey, akPub, + nonce); + + String pcrQuoteMask = PCR_QUOTE_MASK; + + String strNonce = HexUtils.byteArrayToHexString(nonce); + log.info("Sending nonce: {}", strNonce); + log.info("Persisting identity claim of length: {}", identityClaim.length); + + tpm2ProvisionerStateRepository.save(new TPM2ProvisionerState(nonce, identityClaim)); + + if (policySettings.isIgnoreImaEnabled()) { + pcrQuoteMask = PCR_QUOTE_MASK.replace("10,", ""); + } + + // Package response + ProvisionerTpm2.IdentityClaimResponse identityClaimResponse + = ProvisionerTpm2.IdentityClaimResponse.newBuilder() + .setCredentialBlob(blobStr).setPcrMask(pcrQuoteMask) + .setStatus(ProvisionerTpm2.ResponseStatus.PASS) + .build(); + + String identityClaimResponseJsonStringAfterSuccess = ""; + try { + identityClaimResponseJsonStringAfterSuccess = + JsonFormat.printer().print(identityClaimResponse); + } catch (InvalidProtocolBufferException exception) { + log.error("Identity claim response after a successful validation " + + "could not be parsed properly into a json string"); + } + + if (!policySettings.isSaveProtobufToLogNeverEnabled() + && policySettings.isSaveProtobufToLogAlwaysEnabled()) { + + log.info("----------------- Start Of Protobuf Logging Of Identity Claim/Response " + + " After Successful Validation -----------------"); + + log.info("Identity Claim object received after a " + + "successful validation: {}", identityClaimJsonString.isEmpty() + ? claim : identityClaimJsonString); + + log.info("Identity Claim Response object after a " + + "successful validation: {}", identityClaimResponseJsonStringAfterSuccess.isEmpty() + ? identityClaimResponse : identityClaimResponseJsonStringAfterSuccess); + + log.info("----------------- End Of Protobuf Logging Of Identity Claim/Response " + + " After Successful Validation -----------------"); + } + + return identityClaimResponse.toByteArray(); + } else { + log.error("Supply chain validation did not succeed. Result is: {}", validationResult); + // empty response + ProvisionerTpm2.IdentityClaimResponse identityClaimResponse + = ProvisionerTpm2.IdentityClaimResponse.newBuilder() + .setCredentialBlob(blobStr) + .setStatus(ProvisionerTpm2.ResponseStatus.FAIL) + .build(); + + String identityClaimResponseJsonStringAfterFailure = ""; + try { + identityClaimResponseJsonStringAfterFailure = + JsonFormat.printer().print(identityClaimResponse); + } catch (InvalidProtocolBufferException exception) { + log.error("Identity claim response after a failed validation " + + "could not be parsed properly into a json string"); + } + + if (!policySettings.isSaveProtobufToLogNeverEnabled() + && (policySettings.isSaveProtobufToLogAlwaysEnabled() + || policySettings.isSaveProtobufToLogOnFailedValEnabled())) { + log.info("----------------- Start Of Protobuf Logging Of Identity Claim/Response " + + " After Failed Validation -----------------"); + + log.info("Identity Claim object received after a " + + "failed validation: {}", identityClaimJsonString.isEmpty() + ? claim : identityClaimJsonString); + + log.info("Identity Claim Response object after a " + + "failed validation: {}", identityClaimResponseJsonStringAfterFailure.isEmpty() + ? identityClaimResponse : identityClaimResponseJsonStringAfterFailure); + + log.info("----------------- End Of Protobuf Logging Of Identity Claim/Response " + + " After Failed Validation -----------------"); + } + + return identityClaimResponse.toByteArray(); + } + } + + /** + * Performs supply chain validation. + * + * @param claim the identity claim + * @param ekPub the public endorsement key + * @return the {@link AppraisalStatus} of the supply chain validation + */ + private AppraisalStatus.Status doSupplyChainValidation(final ProvisionerTpm2.IdentityClaim claim, + final PublicKey ekPub) throws IOException { + + // attempt to find an endorsement credential to validate + EndorsementCredential endorsementCredential = + credentialManagementService.parseEcFromIdentityClaim(claim, ekPub); + + // attempt to find platform credentials to validate + List platformCredentials = credentialManagementService.parsePcsFromIdentityClaim(claim, + endorsementCredential); + + // Parse and save device info + Device device = deviceManagementService.processDeviceInfo(claim); + +// 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 + // additional information from the DB if information exists + if (platformCredentials.size() == 1) { + List tempList = new LinkedList<>(); + for (PlatformCredential pc : platformCredentials) { + if (pc != null && pc.getPlatformSerial() != null) { + tempList.addAll(certificateRepository.byBoardSerialNumber(pc.getPlatformSerial())); + } + } + + platformCredentials.addAll(tempList); + } + + // store component results objects + for (PlatformCredential platformCredential : platformCredentials) { + List componentResults = + componentResultRepository.findByCertificateSerialNumberAndBoardSerialNumber( + platformCredential.getSerialNumber().toString(), + platformCredential.getPlatformSerial()); + + if (componentResults.isEmpty()) { + savePlatformComponents(platformCredential); + } else { + componentResults.forEach((componentResult) -> { + componentResult.restore(); + componentResult.resetCreateTime(); + componentResultRepository.save(componentResult); + }); + } + } + + // perform supply chain validation + SupplyChainValidationSummary summary = supplyChainValidationService.validateSupplyChain( + endorsementCredential, platformCredentials, device, + componentInfoRepository.findByDeviceName(device.getName())); + device.setSummaryId(summary.getId().toString()); + + // update the validation result in the device + AppraisalStatus.Status validationResult = summary.getOverallValidationResult(); + device.setSupplyChainValidationStatus(validationResult); + this.deviceRepository.save(device); + + return validationResult; + } + + + /** + * Helper method that saves the provided platform certificate's components in the database. + * + * @param certificate certificate + */ + private void savePlatformComponents(final Certificate certificate) throws IOException { + PlatformCredential platformCredential; + + if (certificate instanceof PlatformCredential) { + platformCredential = (PlatformCredential) certificate; + ComponentResult componentResult; + + if (platformCredential.getPlatformConfigurationV1() != null) { + List componentIdentifiers = platformCredential + .getComponentIdentifiers(); + + for (ComponentIdentifier componentIdentifier : componentIdentifiers) { + componentResult = new ComponentResult(platformCredential.getPlatformSerial(), + platformCredential.getSerialNumber().toString(), + platformCredential.getPlatformChainType(), + componentIdentifier); + componentResult.setFailedValidation(false); + componentResult.setDelta(!platformCredential.isPlatformBase()); + componentResultRepository.save(componentResult); + } + } else if (platformCredential.getPlatformConfigurationV2() != null) { + List componentIdentifiersV2 = platformCredential + .getComponentIdentifiersV2(); + + for (ComponentIdentifierV2 componentIdentifierV2 : componentIdentifiersV2) { + componentResult = new ComponentResult(platformCredential.getPlatformSerial(), + platformCredential.getSerialNumber().toString(), + platformCredential.getPlatformChainType(), + componentIdentifierV2); + componentResult.setFailedValidation(false); + componentResult.setDelta(!platformCredential.isPlatformBase()); + componentResultRepository.save(componentResult); + } + } + } + } + + /** + * Helper method that attempts to find all the provided device's components. + * + * @param hostName device's host name + * @param paccorString string representation of the paccor tool output + */ + private void handleDeviceComponents(final String hostName, final String paccorString) { + Map componentInfoMap = new HashMap<>(); + + try { + List componentInfos = SupplyChainCredentialValidator + .getComponentInfoFromPaccorOutput(hostName, paccorString); + + // check the DB for like component infos + List dbComponentInfos = this.componentInfoRepository.findByDeviceName(hostName); + dbComponentInfos.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"); + } + } +} diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/service/SupplyChainValidationService.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/service/SupplyChainValidationService.java similarity index 99% rename from HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/service/SupplyChainValidationService.java rename to HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/service/SupplyChainValidationService.java index 943461db3..b2a5374ce 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/service/SupplyChainValidationService.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/service/SupplyChainValidationService.java @@ -1,4 +1,4 @@ -package hirs.attestationca.persist.service; +package hirs.attestationca.persist.provision.service; import hirs.attestationca.persist.DBManagerException; import hirs.attestationca.persist.entity.ArchivableEntity; @@ -24,6 +24,7 @@ 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.service.ValidationService; import hirs.attestationca.persist.validation.PcrValidator; import hirs.attestationca.persist.validation.SupplyChainCredentialValidator; import lombok.extern.log4j.Log4j2; diff --git a/HIRS_AttestationCA/src/test/java/hirs/attestationca/persist/AttestationCertificateAuthorityServiceTest.java b/HIRS_AttestationCA/src/test/java/hirs/attestationca/persist/AttestationCertificateAuthorityServiceTest.java index f85de7d7c..b1fc84592 100644 --- a/HIRS_AttestationCA/src/test/java/hirs/attestationca/persist/AttestationCertificateAuthorityServiceTest.java +++ b/HIRS_AttestationCA/src/test/java/hirs/attestationca/persist/AttestationCertificateAuthorityServiceTest.java @@ -1,11 +1,10 @@ package hirs.attestationca.persist; import hirs.attestationca.persist.exceptions.CertificateProcessingException; -import hirs.attestationca.persist.provision.AttestationCertificateAuthorityService; -import hirs.attestationca.persist.provision.AttestationCertificateAuthorityServiceImpl; -import hirs.attestationca.persist.provision.CertificateRequestProcessor; -import hirs.attestationca.persist.provision.IdentityClaimProcessor; import hirs.attestationca.persist.provision.helper.ProvisionUtils; +import hirs.attestationca.persist.provision.service.AttestationCertificateAuthorityServiceImpl; +import hirs.attestationca.persist.provision.service.CertificateRequestProcessor; +import hirs.attestationca.persist.provision.service.IdentityClaimProcessor; import hirs.utils.HexUtils; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -124,7 +123,7 @@ public void afterEach() throws Exception { } /** - * Tests {@link AttestationCertificateAuthorityService#processIdentityClaimTpm2(byte[])} + * Tests {@link AttestationCertificateAuthorityServiceImpl#processIdentityClaimTpm2(byte[])} * where the byte array is null or empty. Expects an {@link IllegalArgumentException} to be thrown. */ @Test @@ -157,7 +156,7 @@ public void testProcessIdentityClaimTpm2NullOrEmptyRequest() { } /** - * Tests {@link AttestationCertificateAuthorityService#processIdentityClaimTpm2(byte[])}. + * Tests {@link AttestationCertificateAuthorityServiceImpl#processIdentityClaimTpm2(byte[])}. */ @Test public void testProcessIdentityClaimTpm2() { @@ -176,7 +175,7 @@ public void testProcessIdentityClaimTpm2() { } /** - * Tests {@link AttestationCertificateAuthorityService#processCertificateRequest(byte[])} + * Tests {@link AttestationCertificateAuthorityServiceImpl#processCertificateRequest(byte[])} * where the byte array is null or empty. Expects an {@link IllegalArgumentException} to be thrown. */ @Test @@ -209,7 +208,7 @@ public void testProcessCertificateRequestNullOrEmptyRequest() { } /** - * Tests {@link AttestationCertificateAuthorityService#processCertificateRequest(byte[])} + * Tests {@link AttestationCertificateAuthorityServiceImpl#processCertificateRequest(byte[])} * where the byte array is invalid. Expects a {@link CertificateProcessingException} to be thrown. */ @Test @@ -230,7 +229,7 @@ public void testProcessCertificateRequestProcessorDeserializationError() { } /** - * Tests {@link AttestationCertificateAuthorityService#processCertificateRequest(byte[])}. + * Tests {@link AttestationCertificateAuthorityServiceImpl#processCertificateRequest(byte[])}. */ @Test public void testProcessCertificateRequest() { @@ -249,7 +248,7 @@ public void testProcessCertificateRequest() { } /** - * Tests {@link AttestationCertificateAuthorityService#getLeafACACertPublicKey()}. + * Tests {@link AttestationCertificateAuthorityServiceImpl#getLeafACACertPublicKey()}. */ @Test public void testGetPublicKey() { diff --git a/HIRS_AttestationCA/src/test/java/hirs/attestationca/persist/provision/helper/CredentialManagementHelperTest.java b/HIRS_AttestationCA/src/test/java/hirs/attestationca/persist/provision/helper/CredentialManagementServiceTest.java similarity index 60% rename from HIRS_AttestationCA/src/test/java/hirs/attestationca/persist/provision/helper/CredentialManagementHelperTest.java rename to HIRS_AttestationCA/src/test/java/hirs/attestationca/persist/provision/helper/CredentialManagementServiceTest.java index dfed13110..f9f7c3133 100644 --- a/HIRS_AttestationCA/src/test/java/hirs/attestationca/persist/provision/helper/CredentialManagementHelperTest.java +++ b/HIRS_AttestationCA/src/test/java/hirs/attestationca/persist/provision/helper/CredentialManagementServiceTest.java @@ -2,30 +2,34 @@ import hirs.attestationca.persist.entity.manager.CertificateRepository; import hirs.attestationca.persist.entity.userdefined.Certificate; +import hirs.attestationca.persist.provision.service.CredentialManagementService; import org.apache.commons.io.IOUtils; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import java.io.FileInputStream; import java.io.IOException; +import java.util.Objects; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.verify; /** - * Unit tests for {@see CredentialManagementHelper}. + * Unit tests for {@see CredentialManagementService}. */ -public class CredentialManagementHelperTest { +public class CredentialManagementServiceTest { - private static final String EK_HEADER_TRUNCATED - = "/certificates/nuc-1/ek_cert_7_byte_header_removed.cer"; + private static final String EK_HEADER_TRUNCATED = "/certificates/nuc-1/ek_cert_7_byte_header_removed.cer"; - private static final String EK_UNTOUCHED - = "/certificates/nuc-1/ek_cert_untouched.cer"; + private static final String EK_UNTOUCHED = "/certificates/nuc-1/ek_cert_untouched.cer"; + + @InjectMocks + private CredentialManagementService credentialManagementService; @Mock private CertificateRepository certificateRepository; @@ -40,7 +44,6 @@ public class CredentialManagementHelperTest { */ @BeforeEach public void setUp() { - //certificateRepository = mock(CertificateRepository.class); mocks = MockitoAnnotations.openMocks(this); } @@ -56,27 +59,13 @@ public void tearDown() throws Exception { } } - /** - * Tests exception generated if providing a null cert repository. - * - * @throws IOException if an IO error occurs - */ - @Test - public void processNullCertRep() throws IOException { - // use valid EK byte array - String path = CredentialManagementHelperTest.class.getResource(EK_UNTOUCHED).getPath(); - byte[] ekBytes = IOUtils.toByteArray(new FileInputStream(path)); - assertThrows(IllegalArgumentException.class, () -> - CredentialManagementHelper.storeEndorsementCredential(null, ekBytes, "testName")); - } - /** * Tests exception generated when providing a null EK byte array. */ @Test public void processNullEndorsementCredential() { assertThrows(IllegalArgumentException.class, () -> - CredentialManagementHelper.storeEndorsementCredential(certificateRepository, null, + credentialManagementService.storeEndorsementCredential(null, "testName")); } @@ -86,8 +75,7 @@ public void processNullEndorsementCredential() { @Test public void processEmptyEndorsementCredential() { assertThrows(IllegalArgumentException.class, () -> - CredentialManagementHelper.storeEndorsementCredential( - certificateRepository, new byte[0], "testName")); + credentialManagementService.storeEndorsementCredential(new byte[0], "testName")); } /** @@ -95,10 +83,9 @@ public void processEmptyEndorsementCredential() { */ @Test public void processInvalidEndorsementCredentialCase1() { - byte[] ekBytes = new byte[] {1}; + byte[] ekBytes = new byte[]{1}; assertThrows(IllegalArgumentException.class, () -> - CredentialManagementHelper.storeEndorsementCredential( - certificateRepository, ekBytes, "testName")); + credentialManagementService.storeEndorsementCredential(ekBytes, "testName")); } /** @@ -106,9 +93,9 @@ public void processInvalidEndorsementCredentialCase1() { */ @Test public void processInvalidEndorsementCredentialCase2() { - byte[] ekBytes = new byte[] {1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0}; + byte[] ekBytes = new byte[]{1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0}; assertThrows(IllegalArgumentException.class, () -> - CredentialManagementHelper.storeEndorsementCredential(certificateRepository, ekBytes, + credentialManagementService.storeEndorsementCredential(ekBytes, "testName")); } @@ -119,10 +106,10 @@ public void processInvalidEndorsementCredentialCase2() { */ @Test public void parseUntouchedEndorsementCredential() throws IOException { - String path = CredentialManagementHelperTest.class.getResource(EK_UNTOUCHED).getPath(); + String path = Objects.requireNonNull(CredentialManagementServiceTest.class.getResource(EK_UNTOUCHED)).getPath(); byte[] ekBytes = IOUtils.toByteArray(new FileInputStream(path)); - CredentialManagementHelper.storeEndorsementCredential(certificateRepository, ekBytes, "testName"); + credentialManagementService.storeEndorsementCredential(ekBytes, "testName"); verify(certificateRepository).save(any(Certificate.class)); } @@ -133,11 +120,11 @@ public void parseUntouchedEndorsementCredential() throws IOException { */ @Test public void parseHeaderTruncatedEndorsementCredential() throws IOException { - String path = CredentialManagementHelperTest.class.getResource(EK_HEADER_TRUNCATED) + String path = Objects.requireNonNull(CredentialManagementServiceTest.class.getResource(EK_HEADER_TRUNCATED)) .getPath(); byte[] ekBytes = IOUtils.toByteArray(new FileInputStream(path)); - CredentialManagementHelper.storeEndorsementCredential(certificateRepository, ekBytes, "testName"); + credentialManagementService.storeEndorsementCredential(ekBytes, "testName"); verify(certificateRepository).save(any(Certificate.class)); } } diff --git a/HIRS_AttestationCAPortal/src/main/resources/application.properties b/HIRS_AttestationCAPortal/src/main/resources/application.properties index f75e7b107..0a5040c45 100644 --- a/HIRS_AttestationCAPortal/src/main/resources/application.properties +++ b/HIRS_AttestationCAPortal/src/main/resources/application.properties @@ -33,7 +33,6 @@ aca.certificates.leaf-three-key-alias=HIRS_leaf_ca3_rsa_3k_sha384 aca.certificates.intermediate-key-alias=HIRS_intermediate_ca_rsa_3k_sha384 aca.certificates.root-key-alias=HIRS_root_ca_rsa_3k_sha384 aca.certificates.validity=3652 -aca.current.public.key.algorithm=rsa # Compression settings server.compression.enabled=true # Compression content types diff --git a/HIRS_AttestationCAPortal/src/main/resources/application.win.properties b/HIRS_AttestationCAPortal/src/main/resources/application.win.properties index 7d39de846..946fb2d2f 100644 --- a/HIRS_AttestationCAPortal/src/main/resources/application.win.properties +++ b/HIRS_AttestationCAPortal/src/main/resources/application.win.properties @@ -39,7 +39,6 @@ aca.certificates.leaf-three-key-alias=HIRS_leaf_ca3_rsa_3k_sha384 aca.certificates.intermediate-key-alias=HIRS_intermediate_ca_rsa_3k_sha384 aca.certificates.root-key-alias=HIRS_root_ca_rsa_3k_sha384 aca.certificates.validity=3652 -aca.current.public.key.algorithm=rsa # Compression settings server.compression.enabled=true # Compression content types diff --git a/package/linux/aca/aca_setup.sh b/package/linux/aca/aca_setup.sh index 054a1d5e2..c7523ea96 100755 --- a/package/linux/aca/aca_setup.sh +++ b/package/linux/aca/aca_setup.sh @@ -221,14 +221,12 @@ fi sed -i '/aca.certificates.leaf-three-key-alias/d' $SPRING_PROP_FILE sed -i '/aca.certificates.intermediate-key-alias/d' $SPRING_PROP_FILE sed -i '/aca.certificates.root-key-alias/d' $SPRING_PROP_FILE - sed -i '/aca.current.public.key.algorithm/d' $SPRING_PROP_FILE if [ "$ACA_ALG" == "rsa" ]; then # Add new lines for aca aliases for the RSA public key algorithm { echo "aca.certificates.leaf-three-key-alias=HIRS_leaf_ca3_rsa_3k_sha384_key" echo "aca.certificates.intermediate-key-alias=HIRS_intermediate_ca_rsa_3k_sha384_key" echo "aca.certificates.root-key-alias=HIRS_root_ca_rsa_3k_sha384_key" - echo "aca.current.public.key.algorithm=rsa" } >> $SPRING_PROP_FILE elif [ "$ACA_ALG" == "ecc" ]; then { @@ -236,7 +234,6 @@ elif [ "$ACA_ALG" == "ecc" ]; then echo "aca.certificates.leaf-three-key-alias=HIRS_leaf_ca3_ecc_512_sha384_key" echo "aca.certificates.intermediate-key-alias=HIRS_intermediate_ca_ecc_512_sha384_key" echo "aca.certificates.root-key-alias=HIRS_root_ca_ecc_512_sha384_key" - echo "aca.current.public.key.algorithm=ecc" } >> $SPRING_PROP_FILE fi From fc79f209d4b1d85dbd15a53ce037d1a2d6df9c88 Mon Sep 17 00:00:00 2001 From: ThatSilentCoder <184309164+ThatSilentCoder@users.noreply.github.com> Date: Wed, 4 Mar 2026 17:14:41 -0500 Subject: [PATCH 11/25] v3.1_issue_1090: Made some name changes to the service classes, refactored the device info processor service so now there isnt one huge monolith that handles the device info parsing. Will take on more refactoring for other intricate parts in future commits. --- ...tationCertificateAuthorityServiceImpl.java | 22 +- ...> CertificateRequestProcessorService.java} | 19 +- .../service/CredentialManagementService.java | 18 +- ...e.java => DeviceInfoProcessorService.java} | 501 ++++++++++-------- ...ava => IdentityClaimProcessorService.java} | 18 +- ...tationCertificateAuthorityServiceTest.java | 24 +- 6 files changed, 318 insertions(+), 284 deletions(-) rename HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/service/{CertificateRequestProcessor.java => CertificateRequestProcessorService.java} (96%) rename HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/service/{DeviceManagementService.java => DeviceInfoProcessorService.java} (51%) rename HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/service/{IdentityClaimProcessor.java => IdentityClaimProcessorService.java} (97%) diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/service/AttestationCertificateAuthorityServiceImpl.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/service/AttestationCertificateAuthorityServiceImpl.java index 70f6b4cae..dc013c12b 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/service/AttestationCertificateAuthorityServiceImpl.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/service/AttestationCertificateAuthorityServiceImpl.java @@ -10,21 +10,21 @@ @Service @Log4j2 public class AttestationCertificateAuthorityServiceImpl implements AttestationCertificateAuthorityService { - private final CertificateRequestProcessor certificateRequestProcessor; - private final IdentityClaimProcessor identityClaimProcessor; + private final CertificateRequestProcessorService certificateRequestProcessorService; + private final IdentityClaimProcessorService identityClaimProcessorService; /** * Constructor. * - * @param certificateRequestProcessor certificate request processor service - * @param identityClaimProcessor identity claim processor service + * @param certificateRequestProcessorService certificate request processor service + * @param identityClaimProcessorService identity claim processor service */ @Autowired public AttestationCertificateAuthorityServiceImpl( - final CertificateRequestProcessor certificateRequestProcessor, - final IdentityClaimProcessor identityClaimProcessor) { - this.certificateRequestProcessor = certificateRequestProcessor; - this.identityClaimProcessor = identityClaimProcessor; + final CertificateRequestProcessorService certificateRequestProcessorService, + final IdentityClaimProcessorService identityClaimProcessorService) { + this.certificateRequestProcessorService = certificateRequestProcessorService; + this.identityClaimProcessorService = identityClaimProcessorService; } /** @@ -34,7 +34,7 @@ public AttestationCertificateAuthorityServiceImpl( * @return processed identity claim response */ public byte[] processIdentityClaimTpm2(final byte[] identityClaim) { - return this.identityClaimProcessor.processIdentityClaimTpm2(identityClaim); + return this.identityClaimProcessorService.processIdentityClaimTpm2(identityClaim); } /** @@ -44,7 +44,7 @@ public byte[] processIdentityClaimTpm2(final byte[] identityClaim) { * @return processed certificate request response */ public byte[] processCertificateRequest(final byte[] certificateRequest) { - return this.certificateRequestProcessor.processCertificateRequest(certificateRequest); + return this.certificateRequestProcessorService.processCertificateRequest(certificateRequest); } /** @@ -53,6 +53,6 @@ public byte[] processCertificateRequest(final byte[] certificateRequest) { * @return encoded public key of the leaf certificate */ public byte[] getLeafACACertPublicKey() { - return this.certificateRequestProcessor.getLeafACACertificatePublicKey(); + return this.certificateRequestProcessorService.getLeafACACertificatePublicKey(); } } diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/service/CertificateRequestProcessor.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/service/CertificateRequestProcessorService.java similarity index 96% rename from HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/service/CertificateRequestProcessor.java rename to HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/service/CertificateRequestProcessorService.java index cd32cc0aa..cd8e81afc 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/service/CertificateRequestProcessor.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/service/CertificateRequestProcessorService.java @@ -49,7 +49,7 @@ @Service @Log4j2 -public class CertificateRequestProcessor { +public class CertificateRequestProcessorService { private final SupplyChainValidationService supplyChainValidationService; private final CredentialManagementService credentialManagementService; private final DeviceRepository deviceRepository; @@ -72,14 +72,15 @@ public class CertificateRequestProcessor { * @param policyRepository db connector for policies. */ @Autowired - public CertificateRequestProcessor(final SupplyChainValidationService supplyChainValidationService, - final CredentialManagementService credentialManagementService, - final DeviceRepository deviceRepository, - final TPM2ProvisionerStateRepository tpm2ProvisionerStateRepository, - final PrivateKey privateKey, - @Qualifier("leafACACert") final X509Certificate acaCertificate, - @Value("${aca.certificates.validity}") final int certificateValidityInDays, - final PolicyRepository policyRepository) { + public CertificateRequestProcessorService(final SupplyChainValidationService supplyChainValidationService, + final CredentialManagementService credentialManagementService, + final DeviceRepository deviceRepository, + final TPM2ProvisionerStateRepository tpm2ProvisionerStateRepository, + final PrivateKey privateKey, + @Qualifier("leafACACert") final X509Certificate acaCertificate, + @Value("${aca.certificates.validity}") + final int certificateValidityInDays, + final PolicyRepository policyRepository) { this.credentialManagementService = credentialManagementService; this.certificateValidityInDays = certificateValidityInDays; this.supplyChainValidationService = supplyChainValidationService; diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/service/CredentialManagementService.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/service/CredentialManagementService.java index f983bc894..2aafa5230 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/service/CredentialManagementService.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/service/CredentialManagementService.java @@ -131,9 +131,8 @@ public EndorsementCredential storeEndorsementCredential(final byte[] endorsement } if (endorsementBytes.length <= 1) { - throw new IllegalArgumentException( - String.format("%d-length byte array given for endorsement credential", - endorsementBytes.length) + throw new IllegalArgumentException(String.format("%d-length byte array given for endorsement credential", + endorsementBytes.length) ); } @@ -141,8 +140,7 @@ public EndorsementCredential storeEndorsementCredential(final byte[] endorsement EndorsementCredential endorsementCredential; try { - endorsementCredential = EndorsementCredential - .parseWithPossibleHeader(endorsementBytes); + endorsementCredential = EndorsementCredential.parseWithPossibleHeader(endorsementBytes); } catch (IllegalArgumentException iae) { log.error(iae.getMessage()); throw iae; @@ -183,9 +181,7 @@ public PlatformCredential storePlatformCredential(final byte[] platformBytes, fi if (platformBytes.length == 0) { log.error("The provided platform credential byte array is null."); - throw new IllegalArgumentException( - "zero-length byte array given for platform credential" - ); + throw new IllegalArgumentException("zero-length byte array given for platform credential"); } log.info("Parsing Platform Credential of length {}", platformBytes.length); @@ -195,8 +191,7 @@ public PlatformCredential storePlatformCredential(final byte[] platformBytes, fi PlatformCredential.parseWithPossibleHeader(platformBytes); if (platformCredential == null) { - log.error("The platform credential that was parsed with the provided" - + "byte array was null"); + log.error("The platform credential that was parsed with the provided byte array was null"); return null; } @@ -239,8 +234,7 @@ public PlatformCredential storePlatformCredential(final byte[] platformBytes, fi log.error("Error parsing platform credential", e); } - log.error("Due to an exception being thrown while " - + " attempting to store platform certificate(s) " + log.error("Due to an exception being thrown while attempting to store platform certificate(s) " + "this method will return a null platform certificate."); return null; } diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/service/DeviceManagementService.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/service/DeviceInfoProcessorService.java similarity index 51% rename from HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/service/DeviceManagementService.java rename to HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/service/DeviceInfoProcessorService.java index ded26b257..4ca101168 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/service/DeviceManagementService.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/service/DeviceInfoProcessorService.java @@ -23,7 +23,6 @@ import hirs.utils.enums.DeviceInfoEnums; import hirs.utils.tpm.eventlog.TCGEventLog; import hirs.utils.tpm.eventlog.TpmPcrEvent; -import jakarta.xml.bind.UnmarshalException; import lombok.extern.log4j.Log4j2; import org.apache.commons.codec.binary.Hex; import org.springframework.beans.factory.annotation.Autowired; @@ -43,27 +42,32 @@ import java.util.List; import java.util.Map; import java.util.Optional; -import java.util.UUID; -import java.util.regex.Matcher; import java.util.regex.Pattern; +/** + * + */ @Service @Log4j2 -public class DeviceManagementService { +public class DeviceInfoProcessorService { private static final int NUM_OF_VARIABLES = 5; private static final int MAC_BYTES = 6; + private static final String SUPPORT_RIM_FILE_PATTERN = "(\\S+(\\.(?i)(rimpcr|rimel|bin|log))$)"; private final DeviceRepository deviceRepository; private final ReferenceManifestRepository referenceManifestRepository; private final ReferenceDigestValueRepository referenceDigestValueRepository; + private final MessageDigest messageDigest; @Autowired - public DeviceManagementService(final DeviceRepository deviceRepository, - final ReferenceManifestRepository referenceManifestRepository, - final ReferenceDigestValueRepository referenceDigestValueRepository) { + public DeviceInfoProcessorService(final DeviceRepository deviceRepository, + final ReferenceManifestRepository referenceManifestRepository, + final ReferenceDigestValueRepository referenceDigestValueRepository) + throws NoSuchAlgorithmException { this.deviceRepository = deviceRepository; this.referenceManifestRepository = referenceManifestRepository; this.referenceDigestValueRepository = referenceDigestValueRepository; + this.messageDigest = MessageDigest.getInstance("SHA-256"); } /** @@ -82,9 +86,9 @@ public Device processDeviceInfo(final ProvisionerTpm2.IdentityClaim claim) { } if (deviceInfoReport == null) { - log.error("Failed to deserialize Device Info Report"); - throw new IdentityProcessingException("Device Info Report failed to deserialize " - + "from Identity Claim"); + final String errorMsg = "Failed to parse device info from Protobuf identity claim."; + log.error(errorMsg); + throw new IdentityProcessingException(errorMsg); } log.info("Processing Device Info Report"); @@ -106,7 +110,7 @@ public Device processDeviceInfo(final ProvisionerTpm2.IdentityClaim claim) { } /** - * Helper method that creates a Device Info Report objec using the provided protobuf identity claim's device info. + * Helper method that creates a Device Info Report object using the provided protobuf identity claim's device info. * * @param protoIdentityClaim the protobuf serialized identity claim containing the device info * @return {@link DeviceInfoReport} @@ -124,8 +128,6 @@ private DeviceInfoReport parseDeviceInfo(final ProvisionerTpm2.IdentityClaim pro // Get Hardware info HardwareInfo hardwareInfo = getHardwareInfo(deviceInfoProto.getHw()); - retrieveDeviceInfoFromRIMs(deviceInfoProto, hardwareInfo); - // Get TPM info, currently unimplemented TPMInfo tpmInfo = new TPMInfo(DeviceInfoEnums.NOT_SPECIFIED, (short) 0, @@ -144,7 +146,9 @@ private DeviceInfoReport parseDeviceInfo(final ProvisionerTpm2.IdentityClaim pro // Get OS info OSInfo osInfo = getOSInfo(deviceInfoProto.getOs()); - // Create final report + updateRIMSUsingDeviceInfo(deviceInfoProto, hardwareInfo); + + // Create final device info report DeviceInfoReport dvReport = new DeviceInfoReport(networkInfo, osInfo, firmwareInfo, hardwareInfo, tpmInfo, protoIdentityClaim.getClientVersion()); dvReport.setPaccorOutputString(protoIdentityClaim.getPaccorOutput()); @@ -153,12 +157,12 @@ private DeviceInfoReport parseDeviceInfo(final ProvisionerTpm2.IdentityClaim pro } /** - * Helper method that creates a hardware info object using the provided protobuf's version of hardware info. + * Helper method that creates a {@link HardwareInfo} object using the provided Protobuf's version of Hardware Info. * * @param hardwareInfoProto Protobuf's version of Hardware Info * @return {@link HardwareInfo} */ - private HardwareInfo getHardwareInfo(ProvisionerTpm2.HardwareInfo hardwareInfoProto) { + private HardwareInfo getHardwareInfo(final ProvisionerTpm2.HardwareInfo hardwareInfoProto) { // Make sure chassis info has at least one chassis String firstChassisSerialNumber = DeviceInfoEnums.NOT_SPECIFIED; @@ -178,10 +182,12 @@ private HardwareInfo getHardwareInfo(ProvisionerTpm2.HardwareInfo hardwareInfoPr } /** + * Helper method that creates a {@link NetworkInfo} object using the provided Protobuf's version of Network Info. + * * @param networkInfoProto Protobuf's version of Network Info * @return {@link NetworkInfo} */ - private NetworkInfo getNetworkInfo(ProvisionerTpm2.NetworkInfo networkInfoProto) { + private NetworkInfo getNetworkInfo(final ProvisionerTpm2.NetworkInfo networkInfoProto) { InetAddress ip = null; try { ip = InetAddress.getByName(networkInfoProto.getIpAddress()); @@ -192,6 +198,7 @@ private NetworkInfo getNetworkInfo(ProvisionerTpm2.NetworkInfo networkInfoProto) // convert mac hex string to byte values byte[] macAddressBytes = new byte[MAC_BYTES]; + Integer hex; if (macAddressParts.length == MAC_BYTES) { for (int i = 0; i < MAC_BYTES; i++) { @@ -204,19 +211,23 @@ private NetworkInfo getNetworkInfo(ProvisionerTpm2.NetworkInfo networkInfoProto) } /** + * Helper method that creates an {@link OSInfo} object using the provided Protobuf's version of hardware info. + * * @param osInfoProto Protobuf's version of OS Info * @return {@link OSInfo} */ - private OSInfo getOSInfo(ProvisionerTpm2.OsInfo osInfoProto) { + private OSInfo getOSInfo(final ProvisionerTpm2.OsInfo osInfoProto) { return new OSInfo(osInfoProto.getOsName(), osInfoProto.getOsVersion(), osInfoProto.getOsArch(), osInfoProto.getDistribution(), osInfoProto.getDistributionRelease()); } /** + * Helper method that creates a {@link FirmwareInfo} object using the provided Protobuf's version of firmware info. + * * @param firmwareInfoProto Protobuf's version of Firmware Info * @return {@link FirmwareInfo} */ - private FirmwareInfo getFirmwareInfo(ProvisionerTpm2.FirmwareInfo firmwareInfoProto) { + private FirmwareInfo getFirmwareInfo(final ProvisionerTpm2.FirmwareInfo firmwareInfoProto) { return new FirmwareInfo(firmwareInfoProto.getBiosVendor(), firmwareInfoProto.getBiosVersion(), firmwareInfoProto.getBiosReleaseDate()); } @@ -224,254 +235,232 @@ private FirmwareInfo getFirmwareInfo(ProvisionerTpm2.FirmwareInfo firmwareInfoPr /** * @param deviceInfoProto * @param hw - * @throws NoSuchAlgorithmException */ - private void retrieveDeviceInfoFromRIMs(ProvisionerTpm2.DeviceInfo deviceInfoProto, HardwareInfo hw) - throws NoSuchAlgorithmException { - + private void updateRIMSUsingDeviceInfo(ProvisionerTpm2.DeviceInfo deviceInfoProto, HardwareInfo hw) { // check for RIM Base and Support files, if they don't exist in the database, load them final String defaultClientName = String.format("%s_%s", deviceInfoProto.getHw().getManufacturer(), deviceInfoProto.getHw().getProductName()); - BaseReferenceManifest baseRim = null; - SupportReferenceManifest supportRim = null; - EventLogMeasurements integrityMeasurements; - boolean isReplacement = false; - String replacementRimId = ""; - String tagId = ""; - String fileName = ""; - Pattern pattern = Pattern.compile("(\\S+(\\.(?i)(rimpcr|rimel|bin|log))$)"); - Matcher matcher; - MessageDigest messageDigest = MessageDigest.getInstance("SHA-256"); + final String deviceInfoHostName = deviceInfoProto.getNw().getHostname(); + if (deviceInfoProto.getSwidfileCount() > 0) { - for (ByteString swidFile : deviceInfoProto.getSwidfileList()) { - try { - baseRim = (BaseReferenceManifest) referenceManifestRepository.findByBase64Hash(Base64.getEncoder() - .encodeToString(messageDigest.digest(swidFile.toByteArray()))); + updateBaseRIMSUsingDeviceInfo(defaultClientName, deviceInfoProto); + } else { + log.warn("Device {} did not send SWID tag files...", deviceInfoHostName); + } - if (baseRim == null) { - /* - Either the swidFile does not have a corresponding base RIM in the backend - or it was deleted. Check if there is a replacement by comparing tagId against - all other base RIMs, and then set the corresponding support rim's deviceName. - */ - baseRim = new BaseReferenceManifest(String.format("%s.swidtag", defaultClientName), - swidFile.toByteArray()); - - List baseRims = referenceManifestRepository.findAllBaseRims(); - - for (BaseReferenceManifest bRim : baseRims) { - if (bRim.getTagId().equals(baseRim.getTagId())) { - baseRim = bRim; - replacementRimId = baseRim.getAssociatedRim().toString(); - isReplacement = true; - break; - } - } - baseRim.setDeviceName(deviceInfoProto.getNw().getHostname()); - this.referenceManifestRepository.save(baseRim); - } else if (baseRim.isArchived()) { + if (deviceInfoProto.getLogfileCount() > 0) { + updateSupportRIMSUsingDeviceInfo(defaultClientName, deviceInfoProto); + } else { + log.warn("Device {} did not send Support RIM files...", deviceInfoHostName); + } + + updateAllBaseAndSupportRIMS(deviceInfoProto); + + generateDigestRecords(hw.getManufacturer(), hw.getProductName()); + + if (deviceInfoProto.hasLivelog()) { + updateSupportRIMSUsingEventLogAndDeviceInfo(deviceInfoProto); + } else { + log.warn("Device {} did not send bios measurement log...", deviceInfoHostName); + } + } + + /** + * @param defaultClientName + * @param deviceInfoProto + */ + private void updateBaseRIMSUsingDeviceInfo(final String defaultClientName, + final ProvisionerTpm2.DeviceInfo deviceInfoProto) { + + final List baseRims = referenceManifestRepository.findAllBaseRims(); + final List unarchivedRims = referenceManifestRepository.findByArchiveFlag(false); + final String deviceHostName = deviceInfoProto.getNw().getHostname(); + + log.info("Device {} sent SWID tag files", deviceHostName); + + for (ByteString swidFile : deviceInfoProto.getSwidfileList()) { + try { + final String swidFileHash = + Base64.getEncoder().encodeToString(messageDigest.digest(swidFile.toByteArray())); + + final BaseReferenceManifest baseRim = + (BaseReferenceManifest) referenceManifestRepository.findByBase64Hash(swidFileHash); + /* + Either the swidFile does not have a corresponding base RIM in the backend + or it was deleted. Check if there is a replacement by comparing tagId against + all other base RIMs, and then set the corresponding support rim's deviceName. */ + if (baseRim == null) { + final BaseReferenceManifest replacementBaseRIM = + new BaseReferenceManifest(String.format("%s.swidtag", defaultClientName), + swidFile.toByteArray()); + replacementBaseRIM.setDeviceName(deviceHostName); + + Optional matchedReplacementBaseRIMOptional = baseRims.stream() + .filter(bRim -> + bRim.getTagId().equals(replacementBaseRIM.getTagId())) + .findFirst(); + + // if there is a match, save the matched base RIM + if (matchedReplacementBaseRIMOptional.isPresent()) { + final BaseReferenceManifest matchedReplacementBaseRIM = + matchedReplacementBaseRIMOptional.get(); + matchedReplacementBaseRIM.setDeviceName(replacementBaseRIM.getDeviceName()); + this.referenceManifestRepository.save(matchedReplacementBaseRIM); + continue; + } + + // otherwise save the replacement base RIM we created + this.referenceManifestRepository.save(replacementBaseRIM); + } else if (baseRim.isArchived()) { /* This block accounts for RIMs that may have been soft-deleted (archived) in an older version of the ACA. */ - List rims = referenceManifestRepository.findByArchiveFlag(false); - for (ReferenceManifest rim : rims) { - if (rim.isBase() && rim.getTagId().equals(baseRim.getTagId()) - && rim.getCreateTime().after(baseRim.getCreateTime())) { - baseRim.setDeviceName(null); - baseRim = (BaseReferenceManifest) rim; - baseRim.setDeviceName(deviceInfoProto.getNw().getHostname()); - } - } - if (baseRim.isArchived()) { - throw new Exception("Unable to locate an unarchived base RIM."); - } else { - this.referenceManifestRepository.save(baseRim); - } - } else { - baseRim.setDeviceName(deviceInfoProto.getNw().getHostname()); - this.referenceManifestRepository.save(baseRim); + // Filter out unarchived base RIMs that match the tagId and are newer than the baseRim + Optional matchedUnarchivedBaseRIMOptional = unarchivedRims.stream() + .filter(rim -> rim.isBase() + && rim.getTagId().equals(baseRim.getTagId()) + && rim.getCreateTime().after(baseRim.getCreateTime())) + .map(rim -> (BaseReferenceManifest) rim) + .findFirst(); + + if (matchedUnarchivedBaseRIMOptional.isEmpty()) { + throw new Exception("Unable to locate an unarchived base RIM."); } - tagId = baseRim.getTagId(); - } catch (UnmarshalException e) { - log.error(e); - } catch (Exception ex) { - log.error("Failed to load base rim: {}", ex.getMessage()); + + final BaseReferenceManifest matchedUnarchivedBaseRIM = matchedUnarchivedBaseRIMOptional.get(); + matchedUnarchivedBaseRIM.setDeviceName(deviceHostName); + this.referenceManifestRepository.save(matchedUnarchivedBaseRIM); + } else { + baseRim.setDeviceName(deviceHostName); + this.referenceManifestRepository.save(baseRim); } + } catch (Exception exception) { + log.error("Failed to process Bsase RIM file for device {}: {}", deviceHostName, + exception.getMessage(), exception); } - } else { - log.warn("{} did not send swid tag file...", deviceInfoProto.getNw().getHostname()); } + } - if (deviceInfoProto.getLogfileCount() > 0) { - for (ByteString logFile : deviceInfoProto.getLogfileList()) { - try { - supportRim = - (SupportReferenceManifest) referenceManifestRepository.findByHexDecHashAndRimType( - Hex.encodeHexString(messageDigest.digest(logFile.toByteArray())), - ReferenceManifest.SUPPORT_RIM); - if (supportRim == null) { - /* - Either the logFile does not have a corresponding support RIM in the backend - or it was deleted. The support RIM for a replacement base RIM is handled - in the previous loop block. - */ - if (isReplacement) { - Optional replacementRim = - referenceManifestRepository.findById(UUID.fromString(replacementRimId)); - if (replacementRim.isPresent()) { - supportRim = (SupportReferenceManifest) replacementRim.get(); - supportRim.setDeviceName(deviceInfoProto.getNw().getHostname()); - } else { - throw new Exception("Unable to locate support RIM " + replacementRimId); - } - } else { - supportRim = new SupportReferenceManifest(String.format("%s.rimel", defaultClientName), + /** + * @param defaultClientName + * @param deviceInfoProto + */ + private void updateSupportRIMSUsingDeviceInfo(final String defaultClientName, + final ProvisionerTpm2.DeviceInfo deviceInfoProto) { + final String deviceHostName = deviceInfoProto.getNw().getHostname(); + + log.info("Device {} sent Support RIM files", deviceHostName); + + final List unarchivedRims = referenceManifestRepository.findByArchiveFlag(false); + + for (ByteString logFile : deviceInfoProto.getLogfileList()) { + try { + final String logFileHash = Hex.encodeHexString(messageDigest.digest(logFile.toByteArray())); + + final SupportReferenceManifest supportRim = + (SupportReferenceManifest) referenceManifestRepository.findByHexDecHashAndRimType( + logFileHash, ReferenceManifest.SUPPORT_RIM); + + if (supportRim == null) { + /* + Either the logFile does not have a corresponding support RIM in the backend + or it was deleted. The support RIM for a replacement base RIM is handled + in the previous loop block. + */ + final SupportReferenceManifest replacementSupportRIM = + new SupportReferenceManifest(String.format("%s.rimel", defaultClientName), logFile.toByteArray()); - // this is a validity check - new TCGEventLog(supportRim.getRimBytes()); - // no issues, continue - supportRim.setPlatformManufacturer(deviceInfoProto.getHw().getManufacturer()); - supportRim.setPlatformModel(deviceInfoProto.getHw().getProductName()); - supportRim.setFileName(String.format("%s_[%s].rimel", defaultClientName, - supportRim.getHexDecHash().substring( - supportRim.getHexDecHash().length() - NUM_OF_VARIABLES))); - } - supportRim.setDeviceName(deviceInfoProto.getNw().getHostname()); - this.referenceManifestRepository.save(supportRim); - } else if (supportRim.isArchived()) { - /* - This block accounts for RIMs that may have been soft-deleted (archived) - in an older version of the ACA. - */ - List rims = referenceManifestRepository.findByArchiveFlag(false); - for (ReferenceManifest rim : rims) { - if (rim.isSupport() + + // this is a validity check + new TCGEventLog(replacementSupportRIM.getRimBytes()); + + // no issues, continue + replacementSupportRIM.setPlatformManufacturer(deviceInfoProto.getHw().getManufacturer()); + replacementSupportRIM.setPlatformModel(deviceInfoProto.getHw().getProductName()); + replacementSupportRIM.setFileName(String.format("%s_[%s].rimel", defaultClientName, + replacementSupportRIM.getHexDecHash().substring( + replacementSupportRIM.getHexDecHash().length() - NUM_OF_VARIABLES))); + replacementSupportRIM.setDeviceName(deviceHostName); + this.referenceManifestRepository.save(replacementSupportRIM); + } else if (supportRim.isArchived()) { + /* + This block accounts for RIMs that may have been soft-deleted (archived) + in an older version of the ACA. + */ + // Filter out unarchived support RIMs that match the tagId and are newer than the support RIM + Optional matchedUnarchivedSupportRIMOptional = unarchivedRims.stream() + .filter(rim -> rim.isSupport() && rim.getTagId().equals(supportRim.getTagId()) - && rim.getCreateTime().after(supportRim.getCreateTime())) { - supportRim.setDeviceName(null); - supportRim = (SupportReferenceManifest) rim; - supportRim.setDeviceName(deviceInfoProto.getNw().getHostname()); - } - } - if (supportRim.isArchived()) { - throw new Exception("Unable to locate an unarchived support RIM."); - } else { - this.referenceManifestRepository.save(supportRim); - } - } else { - supportRim.setDeviceName(deviceInfoProto.getNw().getHostname()); - this.referenceManifestRepository.save(supportRim); + && rim.getCreateTime().after(supportRim.getCreateTime())) + .map(rim -> (SupportReferenceManifest) rim) + .findFirst(); + + if (matchedUnarchivedSupportRIMOptional.isEmpty()) { + throw new Exception("Unable to locate an unarchived support RIM."); } - } catch (IOException ioEx) { - log.error(ioEx); - } catch (Exception ex) { - log.error("Failed to load support rim: {}", ex.getMessage()); + + final SupportReferenceManifest matchedUnarchivedSupportRIM = + matchedUnarchivedSupportRIMOptional.get(); + matchedUnarchivedSupportRIM.setDeviceName(deviceHostName); + this.referenceManifestRepository.save(matchedUnarchivedSupportRIM); + } else { + supportRim.setDeviceName(deviceHostName); + this.referenceManifestRepository.save(supportRim); } + } catch (Exception exception) { + log.error("Failed to process Support RIM file for device {}: {}", deviceHostName, + exception.getMessage(), exception); + } - } else { - log.warn("{} did not send support RIM file...", deviceInfoProto.getNw().getHostname()); } + } + + private void updateAllBaseAndSupportRIMS(ProvisionerTpm2.DeviceInfo deviceInfoProto) { + final Pattern supportRimPattern = Pattern.compile(SUPPORT_RIM_FILE_PATTERN); //update Support RIMs and Base RIMs. for (ByteString swidFile : deviceInfoProto.getSwidfileList()) { - baseRim = (BaseReferenceManifest) referenceManifestRepository - .findByBase64Hash(Base64.getEncoder().encodeToString(messageDigest.digest( - swidFile.toByteArray()))); + final String swidFileHash = + Base64.getEncoder().encodeToString(messageDigest.digest(swidFile.toByteArray())); + + final BaseReferenceManifest baseRim = + (BaseReferenceManifest) referenceManifestRepository.findByBase64Hash(swidFileHash); + if (baseRim != null) { - // get file name to use for (SwidResource swid : baseRim.getFileResources()) { - matcher = pattern.matcher(swid.getName()); - if (matcher.matches()) { - //found the file name - int dotIndex = swid.getName().lastIndexOf("."); - fileName = swid.getName().substring(0, dotIndex); - baseRim.setFileName(String.format("%s.swidtag", - fileName)); + if (supportRimPattern.matcher(swid.getName()).matches()) { + final int dotIndex = swid.getName().lastIndexOf("."); + final String fileName = swid.getName().substring(0, dotIndex); + baseRim.setFileName(String.format("%s.swidtag", fileName)); } // now update support rim - SupportReferenceManifest dbSupport = - (SupportReferenceManifest) referenceManifestRepository - .findByHexDecHashAndRimType(swid.getHashValue(), - ReferenceManifest.SUPPORT_RIM); - if (dbSupport != null) { - dbSupport.setFileName(swid.getName()); - dbSupport.setSwidTagVersion(baseRim.getSwidTagVersion()); - dbSupport.setTagId(baseRim.getTagId()); - dbSupport.setSwidTagVersion(baseRim.getSwidTagVersion()); - dbSupport.setSwidVersion(baseRim.getSwidVersion()); - dbSupport.setSwidPatch(baseRim.isSwidPatch()); - dbSupport.setSwidSupplemental(baseRim.isSwidSupplemental()); - baseRim.setAssociatedRim(dbSupport.getId()); - dbSupport.setUpdated(true); - dbSupport.setAssociatedRim(baseRim.getId()); - this.referenceManifestRepository.save(dbSupport); - } else { + SupportReferenceManifest dbSupportRIM = (SupportReferenceManifest) referenceManifestRepository + .findByHexDecHashAndRimType(swid.getHashValue(), ReferenceManifest.SUPPORT_RIM); + + if (dbSupportRIM == null) { log.warn("Could not locate support RIM with hash {}}", swid.getHashValue()); + continue; } - } - this.referenceManifestRepository.save(baseRim); - } - } - - generateDigestRecords(hw.getManufacturer(), hw.getProductName()); - - if (deviceInfoProto.hasLivelog()) { - log.info("Device sent bios measurement log..."); - fileName = String.format("%s.measurement", deviceInfoProto.getNw().getHostname()); - try { - EventLogMeasurements deviceLiveLog = new EventLogMeasurements(fileName, - deviceInfoProto.getLivelog().toByteArray()); - // find previous version. - integrityMeasurements = referenceManifestRepository.byMeasurementDeviceNameUnarchived( - deviceInfoProto.getNw().getHostname()); - - if (integrityMeasurements != null) { - // Find previous log and archive it - integrityMeasurements.archive(); - this.referenceManifestRepository.save(integrityMeasurements); - } - - List baseRims = referenceManifestRepository.getBaseByManufacturerModel( - deviceInfoProto.getHw().getManufacturer(), - deviceInfoProto.getHw().getProductName()); - integrityMeasurements = deviceLiveLog; - integrityMeasurements.setPlatformManufacturer(deviceInfoProto.getHw().getManufacturer()); - integrityMeasurements.setPlatformModel(deviceInfoProto.getHw().getProductName()); - - if (tagId != null && !tagId.trim().isEmpty()) { - integrityMeasurements.setTagId(tagId); + dbSupportRIM.setFileName(swid.getName()); + dbSupportRIM.setSwidTagVersion(baseRim.getSwidTagVersion()); + dbSupportRIM.setTagId(baseRim.getTagId()); + dbSupportRIM.setSwidTagVersion(baseRim.getSwidTagVersion()); + dbSupportRIM.setSwidVersion(baseRim.getSwidVersion()); + dbSupportRIM.setSwidPatch(baseRim.isSwidPatch()); + dbSupportRIM.setSwidSupplemental(baseRim.isSwidSupplemental()); + dbSupportRIM.setUpdated(true); + dbSupportRIM.setAssociatedRim(baseRim.getId()); + baseRim.setAssociatedRim(dbSupportRIM.getId()); + this.referenceManifestRepository.save(dbSupportRIM); } - integrityMeasurements.setDeviceName(deviceInfoProto.getNw().getHostname()); - - this.referenceManifestRepository.save(integrityMeasurements); - - for (BaseReferenceManifest bRim : baseRims) { - if (bRim != null) { - // pull the base versions of the swidtag and rimel and set the - // event log hash for use during provision - SupportReferenceManifest sBaseRim = referenceManifestRepository - .getSupportRimEntityById(bRim.getAssociatedRim()); - if (sBaseRim != null) { - bRim.setEventLogHash(deviceLiveLog.getHexDecHash()); - sBaseRim.setEventLogHash(deviceLiveLog.getHexDecHash()); - referenceManifestRepository.save(bRim); - referenceManifestRepository.save(sBaseRim); - } else { - log.warn("Could not locate support RIM associated with base RIM {}", bRim.getId()); - } - } - } - } catch (IOException ioEx) { - log.error(ioEx); + this.referenceManifestRepository.save(baseRim); } - } else { - log.warn("{} did not send bios measurement log...", deviceInfoProto.getNw().getHostname()); } } @@ -576,4 +565,56 @@ private void generateDigestRecords(final String manufacturer, final String model } } } + + private void updateSupportRIMSUsingEventLogAndDeviceInfo(ProvisionerTpm2.DeviceInfo deviceInfoProto) { + log.info("Device sent bios measurement log..."); + + final String deviceInfoHostName = deviceInfoProto.getNw().getHostname(); + + final String fileName = String.format("%s.measurement", deviceInfoHostName); + try { + // grab the event log from protobuf's device info + EventLogMeasurements deviceInfoProtoEventLog = new EventLogMeasurements(fileName, + deviceInfoProto.getLivelog().toByteArray()); + + // find the previous event log that's stored in the database. + EventLogMeasurements integrityMeasurements = + referenceManifestRepository.byMeasurementDeviceNameUnarchived(deviceInfoHostName); + + // if the event log does exist in the database + if (integrityMeasurements != null) { + // archive it and update the entity in the database + integrityMeasurements.archive(); + this.referenceManifestRepository.save(integrityMeasurements); + } + + List baseRims = referenceManifestRepository.getBaseByManufacturerModel( + deviceInfoProto.getHw().getManufacturer(), + deviceInfoProto.getHw().getProductName()); + + deviceInfoProtoEventLog.setDeviceName(deviceInfoHostName); + deviceInfoProtoEventLog.setPlatformManufacturer(deviceInfoProto.getHw().getManufacturer()); + deviceInfoProtoEventLog.setPlatformModel(deviceInfoProto.getHw().getProductName()); + this.referenceManifestRepository.save(deviceInfoProtoEventLog); + + for (BaseReferenceManifest bRim : baseRims) { + if (bRim != null) { + // pull the base versions of the swidtag and rimel and set the + // event log hash for use during provision + SupportReferenceManifest sBaseRim = referenceManifestRepository + .getSupportRimEntityById(bRim.getAssociatedRim()); + if (sBaseRim != null) { + bRim.setEventLogHash(deviceInfoProtoEventLog.getHexDecHash()); + sBaseRim.setEventLogHash(deviceInfoProtoEventLog.getHexDecHash()); + referenceManifestRepository.save(bRim); + referenceManifestRepository.save(sBaseRim); + } else { + log.warn("Could not locate support RIM associated with base RIM {}", bRim.getId()); + } + } + } + } catch (Exception exception) { + log.error(exception); + } + } } diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/service/IdentityClaimProcessor.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/service/IdentityClaimProcessorService.java similarity index 97% rename from HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/service/IdentityClaimProcessor.java rename to HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/service/IdentityClaimProcessorService.java index eeb9ea073..51ff31761 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/service/IdentityClaimProcessor.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/service/IdentityClaimProcessorService.java @@ -39,7 +39,7 @@ @Service @Log4j2 -public class IdentityClaimProcessor { +public class IdentityClaimProcessorService { /** * Number of bytes to include in the TPM2.0 nonce. */ @@ -48,7 +48,7 @@ public class IdentityClaimProcessor { private final SupplyChainValidationService supplyChainValidationService; private final CredentialManagementService credentialManagementService; - private final DeviceManagementService deviceManagementService; + private final DeviceInfoProcessorService deviceInfoProcessorService; private final CertificateRepository certificateRepository; private final ComponentResultRepository componentResultRepository; private final ComponentInfoRepository componentInfoRepository; @@ -68,13 +68,13 @@ public class IdentityClaimProcessor { * @param policyRepository policy repository */ @Autowired - public IdentityClaimProcessor( + public IdentityClaimProcessorService( final SupplyChainValidationService supplyChainValidationService, final CredentialManagementService credentialManagementService, final CertificateRepository certificateRepository, final ComponentResultRepository componentResultRepository, final ComponentInfoRepository componentInfoRepository, - final DeviceRepository deviceRepository, DeviceManagementService deviceManagementService, + final DeviceRepository deviceRepository, DeviceInfoProcessorService deviceInfoProcessorService, final TPM2ProvisionerStateRepository tpm2ProvisionerStateRepository, final PolicyRepository policyRepository) { this.supplyChainValidationService = supplyChainValidationService; @@ -83,7 +83,7 @@ public IdentityClaimProcessor( this.componentResultRepository = componentResultRepository; this.componentInfoRepository = componentInfoRepository; this.deviceRepository = deviceRepository; - this.deviceManagementService = deviceManagementService; + this.deviceInfoProcessorService = deviceInfoProcessorService; this.tpm2ProvisionerStateRepository = tpm2ProvisionerStateRepository; this.policyRepository = policyRepository; } @@ -245,11 +245,9 @@ private AppraisalStatus.Status doSupplyChainValidation(final ProvisionerTpm2.Ide endorsementCredential); // Parse and save device info - Device device = deviceManagementService.processDeviceInfo(claim); - -// device.getDeviceInfo().setPaccorOutputString(claim.getPaccorOutput()); - handleDeviceComponents(device.getDeviceInfo().getNetworkInfo().getHostname(), - claim.getPaccorOutput()); + Device device = deviceInfoProcessorService.processDeviceInfo(claim); + + 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 diff --git a/HIRS_AttestationCA/src/test/java/hirs/attestationca/persist/AttestationCertificateAuthorityServiceTest.java b/HIRS_AttestationCA/src/test/java/hirs/attestationca/persist/AttestationCertificateAuthorityServiceTest.java index b1fc84592..bd3dfc7e6 100644 --- a/HIRS_AttestationCA/src/test/java/hirs/attestationca/persist/AttestationCertificateAuthorityServiceTest.java +++ b/HIRS_AttestationCA/src/test/java/hirs/attestationca/persist/AttestationCertificateAuthorityServiceTest.java @@ -3,8 +3,8 @@ import hirs.attestationca.persist.exceptions.CertificateProcessingException; import hirs.attestationca.persist.provision.helper.ProvisionUtils; import hirs.attestationca.persist.provision.service.AttestationCertificateAuthorityServiceImpl; -import hirs.attestationca.persist.provision.service.CertificateRequestProcessor; -import hirs.attestationca.persist.provision.service.IdentityClaimProcessor; +import hirs.attestationca.persist.provision.service.CertificateRequestProcessorService; +import hirs.attestationca.persist.provision.service.IdentityClaimProcessorService; import hirs.utils.HexUtils; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -89,10 +89,10 @@ public class AttestationCertificateAuthorityServiceTest { private AttestationCertificateAuthorityServiceImpl attestationCertificateAuthorityService; @Mock - private CertificateRequestProcessor certificateRequestProcessor; + private CertificateRequestProcessorService certificateRequestProcessorService; @Mock - private IdentityClaimProcessor identityClaimProcessor; + private IdentityClaimProcessorService identityClaimProcessorService; /** * Setups configuration prior to each test method. @@ -131,7 +131,7 @@ public void testProcessIdentityClaimTpm2NullOrEmptyRequest() { final String expectedExceptionMsg = "The IdentityClaim sent by the client cannot be null or empty."; // test 1: test null identity claim - when(identityClaimProcessor.processIdentityClaimTpm2(null)).thenThrow( + when(identityClaimProcessorService.processIdentityClaimTpm2(null)).thenThrow( new IllegalArgumentException(expectedExceptionMsg)); // Act & Assert: Verify that the exception is thrown @@ -145,7 +145,7 @@ public void testProcessIdentityClaimTpm2NullOrEmptyRequest() { // initialize an empty byte array final byte[] emptyArr = {}; - when(identityClaimProcessor.processIdentityClaimTpm2(emptyArr)).thenThrow( + when(identityClaimProcessorService.processIdentityClaimTpm2(emptyArr)).thenThrow( new IllegalArgumentException(expectedExceptionMsg)); // Act & Assert: Verify that the exception is thrown @@ -164,11 +164,11 @@ public void testProcessIdentityClaimTpm2() { final byte[] expectedIdentityClaimResponse = {1, 1, 1, 1, 2, 2, 2, 2}; - when(identityClaimProcessor.processIdentityClaimTpm2(identityClaim)).thenReturn( + when(identityClaimProcessorService.processIdentityClaimTpm2(identityClaim)).thenReturn( expectedIdentityClaimResponse); final byte[] actualCertificateResponse = - identityClaimProcessor.processIdentityClaimTpm2(identityClaim); + identityClaimProcessorService.processIdentityClaimTpm2(identityClaim); // Assert that the byte arrays match assertArrayEquals(expectedIdentityClaimResponse, actualCertificateResponse); @@ -183,7 +183,7 @@ public void testProcessCertificateRequestNullOrEmptyRequest() { final String expectedExceptionMsg = "The CertificateRequest sent by the client cannot be null or empty."; // test 1: test null certificate request - when(certificateRequestProcessor.processCertificateRequest(null)).thenThrow( + when(certificateRequestProcessorService.processCertificateRequest(null)).thenThrow( new IllegalArgumentException(expectedExceptionMsg)); // Act & Assert: Verify that the exception is thrown @@ -197,7 +197,7 @@ public void testProcessCertificateRequestNullOrEmptyRequest() { // initialize an empty byte array final byte[] emptyArr = {}; - when(certificateRequestProcessor.processCertificateRequest(emptyArr)).thenThrow( + when(certificateRequestProcessorService.processCertificateRequest(emptyArr)).thenThrow( new IllegalArgumentException(expectedExceptionMsg)); // Act & Assert: Verify that the exception is thrown @@ -217,7 +217,7 @@ public void testProcessCertificateRequestProcessorDeserializationError() { final byte[] badCertificateRequest = {0, 0, 0, 0, 0, 1, 0, 0}; - when(certificateRequestProcessor.processCertificateRequest(badCertificateRequest)).thenThrow( + when(certificateRequestProcessorService.processCertificateRequest(badCertificateRequest)).thenThrow( new CertificateProcessingException(expectedExceptionMsg)); // Act & Assert: Verify that the exception is thrown @@ -237,7 +237,7 @@ public void testProcessCertificateRequest() { final byte[] expectedCertificateResponse = {1, 1, 1, 1}; - when(certificateRequestProcessor.processCertificateRequest(certificateRequest)).thenReturn( + when(certificateRequestProcessorService.processCertificateRequest(certificateRequest)).thenReturn( expectedCertificateResponse); final byte[] actualCertificateResponse = From 72393a2a0bf80541cf04aa44339bba81d8906327 Mon Sep 17 00:00:00 2001 From: ThatSilentCoder <184309164+ThatSilentCoder@users.noreply.github.com> Date: Thu, 5 Mar 2026 17:46:05 -0500 Subject: [PATCH 12/25] v3.1_issue_1120: Finished refactoring the identityclaimprocessor. neeed to figure how to remove componetinforepo completely since it is only being used once. Same with device repo. --- .ci/system-tests/sys_test_common.sh | 2 +- .../SupplyChainValidationRepository.java | 28 ++ ...java => CertificateManagementService.java} | 112 +++++- .../CertificateRequestProcessorService.java | 18 +- .../service/DeviceInfoProcessorService.java | 327 +++++++++++------- .../IdentityClaimProcessorService.java | 139 +------- ... => CertificateManagementServiceTest.java} | 25 +- 7 files changed, 366 insertions(+), 285 deletions(-) create mode 100644 HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/manager/SupplyChainValidationRepository.java rename HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/service/{CredentialManagementService.java => CertificateManagementService.java} (75%) rename HIRS_AttestationCA/src/test/java/hirs/attestationca/persist/provision/helper/{CredentialManagementServiceTest.java => CertificateManagementServiceTest.java} (76%) diff --git a/.ci/system-tests/sys_test_common.sh b/.ci/system-tests/sys_test_common.sh index a7791eff5..95603b994 100644 --- a/.ci/system-tests/sys_test_common.sh +++ b/.ci/system-tests/sys_test_common.sh @@ -61,7 +61,7 @@ clearAcaDb() { docker exec -i $aca_container mysql -u root -proot -e "use hirs_db; set foreign_key_checks=0; truncate Appraiser; truncate Certificate;truncate Issued_Attestation_Platform_Join_Table;truncate CertificatesUsedToValidate;truncate ComponentAttributeResult; truncate ComponentInfo;truncate ComponentResult;truncate Device;truncate DeviceInfoReport;truncate PortalInfo; - truncate ReferenceDigestValue;truncate ReferenceManifest;truncate Report; + truncate ReferenceDigestValue;truncate ReferenceManifest;truncate Report;truncate SupplyChainValidation; truncate SupplyChainValidationSummary;truncate SupplyChainValidationSummary_SupplyChainValidation; truncate TPM2ProvisionerState;set foreign_key_checks=1;" } diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/manager/SupplyChainValidationRepository.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/manager/SupplyChainValidationRepository.java new file mode 100644 index 000000000..e9446c0f6 --- /dev/null +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/manager/SupplyChainValidationRepository.java @@ -0,0 +1,28 @@ +package hirs.attestationca.persist.entity.manager; + +import hirs.attestationca.persist.entity.userdefined.SupplyChainValidation; +import hirs.attestationca.persist.enums.AppraisalStatus; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.UUID; + +@Repository +public interface SupplyChainValidationRepository extends JpaRepository { + /** + * Query that retrieves a list of supply chain validation using the provided validate type. + * + * @param validationType string representation of the validate type + * @return a list of supply chain validation + */ + List findByValidationType(SupplyChainValidation.ValidationType validationType); + + /** + * Query that retrieves a list of supply chain validation using the provided validation result. + * + * @param validationResult string representation of the validation result + * @return a list of supply chain validation + */ + List findByValidationResult(AppraisalStatus.Status validationResult); +} \ No newline at end of file diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/service/CredentialManagementService.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/service/CertificateManagementService.java similarity index 75% rename from HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/service/CredentialManagementService.java rename to HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/service/CertificateManagementService.java index 2aafa5230..870ed17d0 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/service/CredentialManagementService.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/service/CertificateManagementService.java @@ -4,13 +4,17 @@ import hirs.attestationca.configuration.provisionerTpm2.ProvisionerTpm2; import hirs.attestationca.persist.DBManagerException; import hirs.attestationca.persist.entity.manager.CertificateRepository; +import hirs.attestationca.persist.entity.manager.ComponentResultRepository; import hirs.attestationca.persist.entity.manager.PolicyRepository; 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.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.entity.userdefined.certificate.attributes.V2.ComponentIdentifierV2; import hirs.attestationca.persist.exceptions.CertificateProcessingException; import hirs.attestationca.persist.provision.helper.ProvisionUtils; import lombok.extern.log4j.Log4j2; @@ -30,21 +34,25 @@ */ @Service @Log4j2 -public class CredentialManagementService { +public class CertificateManagementService { private final PolicyRepository policyRepository; private final CertificateRepository certificateRepository; + private final ComponentResultRepository componentResultRepository; /** * Constructor. * - * @param policyRepository policy repository - * @param certificateRepository certificate repository + * @param policyRepository policy repository + * @param certificateRepository certificate repository + * @param componentResultRepository */ @Autowired - private CredentialManagementService(final PolicyRepository policyRepository, - final CertificateRepository certificateRepository) { + private CertificateManagementService(final PolicyRepository policyRepository, + final CertificateRepository certificateRepository, + final ComponentResultRepository componentResultRepository) { this.policyRepository = policyRepository; this.certificateRepository = certificateRepository; + this.componentResultRepository = componentResultRepository; } /** @@ -239,6 +247,36 @@ public PlatformCredential storePlatformCredential(final byte[] platformBytes, fi return null; } + /** + * Stores the Platform Certificate's list of associated component results. + * + * @param platformCredentials list of platform credentials + * @throws IOException if any issues arise from storing the platform credentials components + */ + public void saveOrUpdatePlatformCertificateComponents(List platformCredentials) + throws IOException { + + handleSpecialCaseForPlatformCertificates(platformCredentials); + + // store component results objects + for (PlatformCredential platformCredential : platformCredentials) { + List componentResults = + componentResultRepository.findByCertificateSerialNumberAndBoardSerialNumber( + platformCredential.getSerialNumber().toString(), + platformCredential.getPlatformSerial()); + + if (componentResults.isEmpty()) { + savePlatformCertificateComponents(platformCredential); + } else { + componentResults.forEach((componentResult) -> { + componentResult.restore(); + componentResult.resetCreateTime(); + componentResultRepository.save(componentResult); + }); + } + } + } + /** * Creates an {@link IssuedAttestationCertificate} object and set its corresponding device and persists it. * @@ -257,7 +295,7 @@ public boolean saveAttestationCertificate( final Device device, final boolean ldevID) { List issuedAc; - boolean generateCertificate = true; + boolean generateCertificate; PolicySettings policySettings; Date currentDate = new Date(); int days; @@ -349,6 +387,7 @@ private List getPlatformCredentials(final EndorsementCredent 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 { @@ -358,4 +397,65 @@ private List getPlatformCredentials(final EndorsementCredent return credentials; } + + /** + * There are situations in which the claim is sent with no platform certificates or a platform certificate + * from the tpm which will be deprecated. This is to check what is in the platform object and pull + * additional information from the DB if information exists. + * + * @param platformCredentials list of platform credentials + */ + private void handleSpecialCaseForPlatformCertificates(List platformCredentials) { + + if (platformCredentials.size() == 1) { + List additionalPC = new LinkedList<>(); + PlatformCredential pc = platformCredentials.getFirst(); + if (pc != null && pc.getPlatformSerial() != null) { + additionalPC.addAll(certificateRepository.byBoardSerialNumber(pc.getPlatformSerial())); + } + platformCredentials.addAll(additionalPC); + } + } + + /** + * Helper method that saves the provided platform certificate's components in the database. + * + * @param certificate certificate + */ + private void savePlatformCertificateComponents(final Certificate certificate) throws IOException { + PlatformCredential platformCredential; + + if (certificate instanceof PlatformCredential) { + platformCredential = (PlatformCredential) certificate; + ComponentResult componentResult; + + if (platformCredential.getPlatformConfigurationV1() != null) { + List componentIdentifiers = platformCredential + .getComponentIdentifiers(); + + for (ComponentIdentifier componentIdentifier : componentIdentifiers) { + componentResult = new ComponentResult(platformCredential.getPlatformSerial(), + platformCredential.getSerialNumber().toString(), + platformCredential.getPlatformChainType(), + componentIdentifier); + componentResult.setFailedValidation(false); + componentResult.setDelta(!platformCredential.isPlatformBase()); + componentResultRepository.save(componentResult); + } + } else if (platformCredential.getPlatformConfigurationV2() != null) { + List componentIdentifiersV2 = platformCredential + .getComponentIdentifiersV2(); + + for (ComponentIdentifierV2 componentIdentifierV2 : componentIdentifiersV2) { + componentResult = new ComponentResult(platformCredential.getPlatformSerial(), + platformCredential.getSerialNumber().toString(), + platformCredential.getPlatformChainType(), + componentIdentifierV2); + componentResult.setFailedValidation(false); + componentResult.setDelta(!platformCredential.isPlatformBase()); + componentResultRepository.save(componentResult); + } + } + } + } } diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/service/CertificateRequestProcessorService.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/service/CertificateRequestProcessorService.java index cd8e81afc..5317d400a 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/service/CertificateRequestProcessorService.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/service/CertificateRequestProcessorService.java @@ -51,7 +51,7 @@ @Log4j2 public class CertificateRequestProcessorService { private final SupplyChainValidationService supplyChainValidationService; - private final CredentialManagementService credentialManagementService; + private final CertificateManagementService certificateManagementService; private final DeviceRepository deviceRepository; private final TPM2ProvisionerStateRepository tpm2ProvisionerStateRepository; private final PolicyRepository policyRepository; @@ -63,7 +63,7 @@ public class CertificateRequestProcessorService { * Constructor. * * @param supplyChainValidationService object that is used to run provisioning - * @param credentialManagementService credential management service + * @param certificateManagementService credential management service * @param deviceRepository database connector for Devices. * @param tpm2ProvisionerStateRepository db connector for provisioner state. * @param privateKey private key used for communication authentication @@ -73,7 +73,7 @@ public class CertificateRequestProcessorService { */ @Autowired public CertificateRequestProcessorService(final SupplyChainValidationService supplyChainValidationService, - final CredentialManagementService credentialManagementService, + final CertificateManagementService certificateManagementService, final DeviceRepository deviceRepository, final TPM2ProvisionerStateRepository tpm2ProvisionerStateRepository, final PrivateKey privateKey, @@ -81,7 +81,7 @@ public CertificateRequestProcessorService(final SupplyChainValidationService sup @Value("${aca.certificates.validity}") final int certificateValidityInDays, final PolicyRepository policyRepository) { - this.credentialManagementService = credentialManagementService; + this.certificateManagementService = certificateManagementService; this.certificateValidityInDays = certificateValidityInDays; this.supplyChainValidationService = supplyChainValidationService; this.deviceRepository = deviceRepository; @@ -155,10 +155,10 @@ public byte[] processCertificateRequest(final byte[] certificateRequest) { // Get Endorsement Credential if it exists or was uploaded EndorsementCredential endorsementCredential = - credentialManagementService.parseEcFromIdentityClaim(claim, ekPublicKey); + certificateManagementService.parseEcFromIdentityClaim(claim, ekPublicKey); // Get Platform Credentials if they exist or were uploaded - List platformCredentials = credentialManagementService.parsePcsFromIdentityClaim(claim, + List platformCredentials = certificateManagementService.parsePcsFromIdentityClaim(claim, endorsementCredential); // Get LDevID public key if it exists @@ -221,11 +221,11 @@ public byte[] processCertificateRequest(final byte[] certificateRequest) { tpm2ProvisionerStateRepository.delete(tpm2ProvisionerState); boolean generateAtt = - credentialManagementService.saveAttestationCertificate(derEncodedAttestationCertificate, + certificateManagementService.saveAttestationCertificate(derEncodedAttestationCertificate, endorsementCredential, platformCredentials, device, false); boolean generateLDevID = - credentialManagementService.saveAttestationCertificate(derEncodedLdevidCertificate, + certificateManagementService.saveAttestationCertificate(derEncodedLdevidCertificate, endorsementCredential, platformCredentials, device, true); ProvisionerTpm2.CertificateResponse.Builder certificateResponseBuilder = @@ -287,7 +287,7 @@ public byte[] processCertificateRequest(final byte[] certificateRequest) { ProvisionerTpm2.CertificateResponse. newBuilder().setStatus(ProvisionerTpm2.ResponseStatus.PASS); - boolean generateAtt = credentialManagementService.saveAttestationCertificate( + boolean generateAtt = certificateManagementService.saveAttestationCertificate( derEncodedAttestationCertificate, endorsementCredential, platformCredentials, device, false); diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/service/DeviceInfoProcessorService.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/service/DeviceInfoProcessorService.java index 4ca101168..b390c7d0e 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/service/DeviceInfoProcessorService.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/service/DeviceInfoProcessorService.java @@ -2,11 +2,13 @@ import com.google.protobuf.ByteString; import hirs.attestationca.configuration.provisionerTpm2.ProvisionerTpm2; +import hirs.attestationca.persist.entity.manager.ComponentInfoRepository; import hirs.attestationca.persist.entity.manager.DeviceRepository; import hirs.attestationca.persist.entity.manager.ReferenceDigestValueRepository; import hirs.attestationca.persist.entity.manager.ReferenceManifestRepository; import hirs.attestationca.persist.entity.userdefined.Device; import hirs.attestationca.persist.entity.userdefined.ReferenceManifest; +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; @@ -18,11 +20,13 @@ import hirs.attestationca.persist.entity.userdefined.rim.ReferenceDigestValue; import hirs.attestationca.persist.entity.userdefined.rim.SupportReferenceManifest; import hirs.attestationca.persist.exceptions.IdentityProcessingException; +import hirs.attestationca.persist.validation.SupplyChainCredentialValidator; import hirs.utils.HexUtils; import hirs.utils.SwidResource; import hirs.utils.enums.DeviceInfoEnums; import hirs.utils.tpm.eventlog.TCGEventLog; import hirs.utils.tpm.eventlog.TpmPcrEvent; +import io.micrometer.common.util.StringUtils; import lombok.extern.log4j.Log4j2; import org.apache.commons.codec.binary.Hex; import org.springframework.beans.factory.annotation.Autowired; @@ -45,48 +49,62 @@ import java.util.regex.Pattern; /** - * + * Service class that parses and processes Device information from the Identity Claim and uses it to update the Device + * and RIMs. */ @Service @Log4j2 public class DeviceInfoProcessorService { - private static final int NUM_OF_VARIABLES = 5; private static final int MAC_BYTES = 6; - private static final String SUPPORT_RIM_FILE_PATTERN = "(\\S+(\\.(?i)(rimpcr|rimel|bin|log))$)"; private final DeviceRepository deviceRepository; private final ReferenceManifestRepository referenceManifestRepository; private final ReferenceDigestValueRepository referenceDigestValueRepository; + private final ComponentInfoRepository componentInfoRepository; private final MessageDigest messageDigest; + /** + * Constructor. + * + * @param deviceRepository device repository + * @param referenceManifestRepository reference manifest repository + * @param referenceDigestValueRepository reference digest value repository + * @param componentInfoRepository component info repository + * @throws NoSuchAlgorithmException if any issues arise from creating a {@link MessageDigest} + */ @Autowired public DeviceInfoProcessorService(final DeviceRepository deviceRepository, final ReferenceManifestRepository referenceManifestRepository, - final ReferenceDigestValueRepository referenceDigestValueRepository) + final ReferenceDigestValueRepository referenceDigestValueRepository, + final ComponentInfoRepository componentInfoRepository) throws NoSuchAlgorithmException { this.deviceRepository = deviceRepository; this.referenceManifestRepository = referenceManifestRepository; this.referenceDigestValueRepository = referenceDigestValueRepository; + this.componentInfoRepository = componentInfoRepository; this.messageDigest = MessageDigest.getInstance("SHA-256"); } /** - * Utilizes the identity claim to produce a device info report. + * Creates a {@link DeviceInfoReport} from the identity claim's Device Info and updates the corresponding + * {@link Device}. * - * @param claim identity claim - * @return device info + * @param identityClaim Identity Claim + * @return device */ - public Device processDeviceInfo(final ProvisionerTpm2.IdentityClaim claim) { + public Device processDeviceInfo(final ProvisionerTpm2.IdentityClaim identityClaim) { DeviceInfoReport deviceInfoReport = null; + log.info("Parsing Device Info from the Identity Claim"); + try { - deviceInfoReport = parseDeviceInfo(claim); + deviceInfoReport = parseDeviceInfo(identityClaim); } catch (NoSuchAlgorithmException noSaEx) { log.error(noSaEx); } if (deviceInfoReport == null) { - final String errorMsg = "Failed to parse device info from Protobuf identity claim."; + final String errorMsg = "Failed to parse device info from Protobuf Identity Claim."; log.error(errorMsg); throw new IdentityProcessingException(errorMsg); } @@ -96,9 +114,8 @@ public Device processDeviceInfo(final ProvisionerTpm2.IdentityClaim claim) { // store device and device info report. Device device = null; - if (deviceInfoReport.getNetworkInfo() != null - && deviceInfoReport.getNetworkInfo().getHostname() != null - && !deviceInfoReport.getNetworkInfo().getHostname().isEmpty()) { + if (deviceInfoReport.getNetworkInfo() != null && + !StringUtils.isBlank(deviceInfoReport.getNetworkInfo().getHostname())) { device = this.deviceRepository.findByName(deviceInfoReport.getNetworkInfo().getHostname()); } @@ -106,181 +123,199 @@ public Device processDeviceInfo(final ProvisionerTpm2.IdentityClaim claim) { device = new Device(deviceInfoReport); } device.setDeviceInfo(deviceInfoReport); + + processDeviceComponents(deviceInfoReport.getNetworkInfo().getHostname(), + deviceInfoReport.getPaccorOutputString()); + return this.deviceRepository.save(device); } /** - * Helper method that creates a Device Info Report object using the provided protobuf identity claim's device info. + * Helper method that creates a Device Info Report using the provided Identity Claim's Device Info. * - * @param protoIdentityClaim the protobuf serialized identity claim containing the device info + * @param identityClaim the protobuf serialized Identity Claim containing the device info * @return {@link DeviceInfoReport} */ - private DeviceInfoReport parseDeviceInfo(final ProvisionerTpm2.IdentityClaim protoIdentityClaim) + private DeviceInfoReport parseDeviceInfo(final ProvisionerTpm2.IdentityClaim identityClaim) throws NoSuchAlgorithmException { - ProvisionerTpm2.DeviceInfo deviceInfoProto = protoIdentityClaim.getDv(); - - String pcrValues = ""; - - if (deviceInfoProto.hasPcrslist()) { - pcrValues = deviceInfoProto.getPcrslist().toStringUtf8(); - } + ProvisionerTpm2.DeviceInfo tpmDeviceInfo = identityClaim.getDv(); // Get Hardware info - HardwareInfo hardwareInfo = getHardwareInfo(deviceInfoProto.getHw()); + HardwareInfo hardwareInfo = getHardwareInfo(tpmDeviceInfo.getHw()); - // Get TPM info, currently unimplemented - TPMInfo tpmInfo = new TPMInfo(DeviceInfoEnums.NOT_SPECIFIED, - (short) 0, - (short) 0, - (short) 0, - (short) 0, - pcrValues.getBytes(StandardCharsets.UTF_8), - null, null); + // Get TPM info ( todo Currently unimplemented) + TPMInfo tpmInfo = getTPMInfo(tpmDeviceInfo); // Get Network info - NetworkInfo networkInfo = getNetworkInfo(deviceInfoProto.getNw()); + NetworkInfo networkInfo = getNetworkInfo(tpmDeviceInfo.getNw()); // Get Firmware info - FirmwareInfo firmwareInfo = getFirmwareInfo(deviceInfoProto.getFw()); + FirmwareInfo firmwareInfo = getFirmwareInfo(tpmDeviceInfo.getFw()); // Get OS info - OSInfo osInfo = getOSInfo(deviceInfoProto.getOs()); + OSInfo osInfo = getOSInfo(tpmDeviceInfo.getOs()); - updateRIMSUsingDeviceInfo(deviceInfoProto, hardwareInfo); + getAndUpdateRIMSUsingTPMDeviceInfo(tpmDeviceInfo); // Create final device info report DeviceInfoReport dvReport = new DeviceInfoReport(networkInfo, osInfo, firmwareInfo, hardwareInfo, tpmInfo, - protoIdentityClaim.getClientVersion()); - dvReport.setPaccorOutputString(protoIdentityClaim.getPaccorOutput()); + identityClaim.getClientVersion()); + dvReport.setPaccorOutputString(identityClaim.getPaccorOutput()); return dvReport; } /** - * Helper method that creates a {@link HardwareInfo} object using the provided Protobuf's version of Hardware Info. + * Helper method that creates a {@link TPMInfo} using the provided TPM Device Info ( todo Currently unimplemented) * - * @param hardwareInfoProto Protobuf's version of Hardware Info + * @param tpmDeviceInfo TPM Device Info + * @return {@link TPMInfo} + */ + private TPMInfo getTPMInfo(ProvisionerTpm2.DeviceInfo tpmDeviceInfo) { + String pcrValues = ""; + + if (tpmDeviceInfo.hasPcrslist()) { + pcrValues = tpmDeviceInfo.getPcrslist().toStringUtf8(); + } + + return new TPMInfo(DeviceInfoEnums.NOT_SPECIFIED, + (short) 0, + (short) 0, + (short) 0, + (short) 0, + pcrValues.getBytes(StandardCharsets.UTF_8), + null, null); + } + + /** + * Helper method that creates a {@link HardwareInfo} using the provided TPM Device Info's Hardware Info. + * + * @param tpmHardwareInfo TPM Device Info's Hardware Info * @return {@link HardwareInfo} */ - private HardwareInfo getHardwareInfo(final ProvisionerTpm2.HardwareInfo hardwareInfoProto) { + private HardwareInfo getHardwareInfo(final ProvisionerTpm2.HardwareInfo tpmHardwareInfo) { // Make sure chassis info has at least one chassis String firstChassisSerialNumber = DeviceInfoEnums.NOT_SPECIFIED; - if (hardwareInfoProto.getChassisInfoCount() > 0) { - firstChassisSerialNumber = hardwareInfoProto.getChassisInfo(0).getSerialNumber(); + if (tpmHardwareInfo.getChassisInfoCount() > 0) { + firstChassisSerialNumber = tpmHardwareInfo.getChassisInfo(0).getSerialNumber(); } // Make sure baseboard info has at least one baseboard String firstBaseboardSerialNumber = DeviceInfoEnums.NOT_SPECIFIED; - if (hardwareInfoProto.getBaseboardInfoCount() > 0) { - firstBaseboardSerialNumber = hardwareInfoProto.getBaseboardInfo(0).getSerialNumber(); + if (tpmHardwareInfo.getBaseboardInfoCount() > 0) { + firstBaseboardSerialNumber = tpmHardwareInfo.getBaseboardInfo(0).getSerialNumber(); } - return new HardwareInfo(hardwareInfoProto.getManufacturer(), hardwareInfoProto.getProductName(), - hardwareInfoProto.getProductVersion(), hardwareInfoProto.getSystemSerialNumber(), + return new HardwareInfo(tpmHardwareInfo.getManufacturer(), tpmHardwareInfo.getProductName(), + tpmHardwareInfo.getProductVersion(), tpmHardwareInfo.getSystemSerialNumber(), firstChassisSerialNumber, firstBaseboardSerialNumber); } /** - * Helper method that creates a {@link NetworkInfo} object using the provided Protobuf's version of Network Info. + * Helper method that creates a {@link NetworkInfo} using the provided TPM Device Info's Network Info. * - * @param networkInfoProto Protobuf's version of Network Info + * @param tpmNetworkInfo TPM Device Info's Network Info * @return {@link NetworkInfo} */ - private NetworkInfo getNetworkInfo(final ProvisionerTpm2.NetworkInfo networkInfoProto) { + private NetworkInfo getNetworkInfo(final ProvisionerTpm2.NetworkInfo tpmNetworkInfo) { InetAddress ip = null; try { - ip = InetAddress.getByName(networkInfoProto.getIpAddress()); + ip = InetAddress.getByName(tpmNetworkInfo.getIpAddress()); } catch (UnknownHostException uhEx) { - log.error("Unable to parse IP address: ", uhEx); + log.error("Unable to parse IP address from the TPM Device Info: ", uhEx); } - String[] macAddressParts = networkInfoProto.getMacAddress().split(":"); + String[] macAddressParts = tpmNetworkInfo.getMacAddress().split(":"); // convert mac hex string to byte values byte[] macAddressBytes = new byte[MAC_BYTES]; - Integer hex; if (macAddressParts.length == MAC_BYTES) { for (int i = 0; i < MAC_BYTES; i++) { - hex = HexUtils.hexToInt(macAddressParts[i]); + Integer hex = HexUtils.hexToInt(macAddressParts[i]); macAddressBytes[i] = hex.byteValue(); } } - return new NetworkInfo(networkInfoProto.getHostname(), ip, macAddressBytes); + return new NetworkInfo(tpmNetworkInfo.getHostname(), ip, macAddressBytes); } /** - * Helper method that creates an {@link OSInfo} object using the provided Protobuf's version of hardware info. + * Helper method that creates an {@link OSInfo} using the provided TPM Device Info's OS info. * - * @param osInfoProto Protobuf's version of OS Info + * @param tpmOsInfo TPM Device Info's OS Info * @return {@link OSInfo} */ - private OSInfo getOSInfo(final ProvisionerTpm2.OsInfo osInfoProto) { - return new OSInfo(osInfoProto.getOsName(), osInfoProto.getOsVersion(), osInfoProto.getOsArch(), - osInfoProto.getDistribution(), osInfoProto.getDistributionRelease()); + private OSInfo getOSInfo(final ProvisionerTpm2.OsInfo tpmOsInfo) { + return new OSInfo(tpmOsInfo.getOsName(), tpmOsInfo.getOsVersion(), tpmOsInfo.getOsArch(), + tpmOsInfo.getDistribution(), tpmOsInfo.getDistributionRelease()); } /** - * Helper method that creates a {@link FirmwareInfo} object using the provided Protobuf's version of firmware info. + * Helper method that creates a {@link FirmwareInfo} using the provided TPM Device Info's Firmware Info. * - * @param firmwareInfoProto Protobuf's version of Firmware Info + * @param tpmFirmwareInfo TPM Device Info's Firmware Info * @return {@link FirmwareInfo} */ - private FirmwareInfo getFirmwareInfo(final ProvisionerTpm2.FirmwareInfo firmwareInfoProto) { - return new FirmwareInfo(firmwareInfoProto.getBiosVendor(), firmwareInfoProto.getBiosVersion(), - firmwareInfoProto.getBiosReleaseDate()); + private FirmwareInfo getFirmwareInfo(final ProvisionerTpm2.FirmwareInfo tpmFirmwareInfo) { + return new FirmwareInfo(tpmFirmwareInfo.getBiosVendor(), tpmFirmwareInfo.getBiosVersion(), + tpmFirmwareInfo.getBiosReleaseDate()); } /** - * @param deviceInfoProto - * @param hw + * Helper method that updates all Reference Integrity Manifests (RIMs) using the provided TPM's Device Info. + * + * @param tpmDeviceInfo TPM Device Info */ - private void updateRIMSUsingDeviceInfo(ProvisionerTpm2.DeviceInfo deviceInfoProto, HardwareInfo hw) { + private void getAndUpdateRIMSUsingTPMDeviceInfo(ProvisionerTpm2.DeviceInfo tpmDeviceInfo) { // check for RIM Base and Support files, if they don't exist in the database, load them - final String defaultClientName = String.format("%s_%s", deviceInfoProto.getHw().getManufacturer(), - deviceInfoProto.getHw().getProductName()); - - final String deviceInfoHostName = deviceInfoProto.getNw().getHostname(); + final String defaultClientName = String.format("%s_%s", tpmDeviceInfo.getHw().getManufacturer(), + tpmDeviceInfo.getHw().getProductName()); + final String deviceInfoHostName = tpmDeviceInfo.getNw().getHostname(); - if (deviceInfoProto.getSwidfileCount() > 0) { - updateBaseRIMSUsingDeviceInfo(defaultClientName, deviceInfoProto); + // update base RIMs using the identity claim's device information + if (tpmDeviceInfo.getSwidfileCount() > 0) { + updateBaseRIMSUsingTPMDeviceInfo(defaultClientName, tpmDeviceInfo); } else { log.warn("Device {} did not send SWID tag files...", deviceInfoHostName); } - if (deviceInfoProto.getLogfileCount() > 0) { - updateSupportRIMSUsingDeviceInfo(defaultClientName, deviceInfoProto); + // update support RIMs using the identity claim's device information + if (tpmDeviceInfo.getLogfileCount() > 0) { + updateSupportRIMSUsingTPMDeviceInfo(defaultClientName, tpmDeviceInfo); } else { log.warn("Device {} did not send Support RIM files...", deviceInfoHostName); } - updateAllBaseAndSupportRIMS(deviceInfoProto); + // update both base and support RIMs to ensure updates are consistent + updateBaseSupportRIMSUsingTpmDeviceInfo(tpmDeviceInfo); - generateDigestRecords(hw.getManufacturer(), hw.getProductName()); + generateDigestRecords(tpmDeviceInfo.getHw().getManufacturer(), tpmDeviceInfo.getHw().getProductName()); - if (deviceInfoProto.hasLivelog()) { - updateSupportRIMSUsingEventLogAndDeviceInfo(deviceInfoProto); + // update event log information + if (tpmDeviceInfo.hasLivelog()) { + updateEventLogInfoUsingTPMDeviceInfo(tpmDeviceInfo); } else { - log.warn("Device {} did not send bios measurement log...", deviceInfoHostName); + log.warn("Device {} did not send BIOS measurement log...", deviceInfoHostName); } } /** - * @param defaultClientName - * @param deviceInfoProto + * Helper method that updates the Base RIMs in the database using the information provided by the TPM's Device Info. + * + * @param defaultClientName default client name + * @param tpmDeviceInfo TPM Device Info */ - private void updateBaseRIMSUsingDeviceInfo(final String defaultClientName, - final ProvisionerTpm2.DeviceInfo deviceInfoProto) { - + private void updateBaseRIMSUsingTPMDeviceInfo(final String defaultClientName, + final ProvisionerTpm2.DeviceInfo tpmDeviceInfo) { final List baseRims = referenceManifestRepository.findAllBaseRims(); final List unarchivedRims = referenceManifestRepository.findByArchiveFlag(false); - final String deviceHostName = deviceInfoProto.getNw().getHostname(); + final String deviceHostName = tpmDeviceInfo.getNw().getHostname(); log.info("Device {} sent SWID tag files", deviceHostName); - for (ByteString swidFile : deviceInfoProto.getSwidfileList()) { + for (ByteString swidFile : tpmDeviceInfo.getSwidfileList()) { try { final String swidFileHash = Base64.getEncoder().encodeToString(messageDigest.digest(swidFile.toByteArray())); @@ -288,7 +323,7 @@ private void updateBaseRIMSUsingDeviceInfo(final String defaultClientName, final BaseReferenceManifest baseRim = (BaseReferenceManifest) referenceManifestRepository.findByBase64Hash(swidFileHash); /* - Either the swidFile does not have a corresponding base RIM in the backend + Either the swidFile does not have a corresponding base RIM in the backend, or it was deleted. Check if there is a replacement by comparing tagId against all other base RIMs, and then set the corresponding support rim's deviceName. */ if (baseRim == null) { @@ -314,10 +349,8 @@ private void updateBaseRIMSUsingDeviceInfo(final String defaultClientName, // otherwise save the replacement base RIM we created this.referenceManifestRepository.save(replacementBaseRIM); } else if (baseRim.isArchived()) { - /* - This block accounts for RIMs that may have been soft-deleted (archived) - in an older version of the ACA. - */ + /* This block accounts for RIMs that may have been soft-deleted (archived) + in an older version of the ACA. */ // Filter out unarchived base RIMs that match the tagId and are newer than the baseRim Optional matchedUnarchivedBaseRIMOptional = unarchivedRims.stream() .filter(rim -> rim.isBase() @@ -338,25 +371,29 @@ private void updateBaseRIMSUsingDeviceInfo(final String defaultClientName, this.referenceManifestRepository.save(baseRim); } } catch (Exception exception) { - log.error("Failed to process Bsase RIM file for device {}: {}", deviceHostName, + log.error("Failed to process Base RIM file for device {}: {}", deviceHostName, exception.getMessage(), exception); } } } /** - * @param defaultClientName - * @param deviceInfoProto + * Helper method that updates the Support RIMs in the database using the information provided by the TPM's Device + * Info. + * + * @param defaultClientName default client name + * @param tpmDeviceInfo TPM Device Info */ - private void updateSupportRIMSUsingDeviceInfo(final String defaultClientName, - final ProvisionerTpm2.DeviceInfo deviceInfoProto) { - final String deviceHostName = deviceInfoProto.getNw().getHostname(); + private void updateSupportRIMSUsingTPMDeviceInfo(final String defaultClientName, + final ProvisionerTpm2.DeviceInfo tpmDeviceInfo) { + final String deviceHostName = tpmDeviceInfo.getNw().getHostname(); + final int NUM_OF_VARIABLES = 5; log.info("Device {} sent Support RIM files", deviceHostName); final List unarchivedRims = referenceManifestRepository.findByArchiveFlag(false); - for (ByteString logFile : deviceInfoProto.getLogfileList()) { + for (ByteString logFile : tpmDeviceInfo.getLogfileList()) { try { final String logFileHash = Hex.encodeHexString(messageDigest.digest(logFile.toByteArray())); @@ -365,11 +402,9 @@ private void updateSupportRIMSUsingDeviceInfo(final String defaultClientName, logFileHash, ReferenceManifest.SUPPORT_RIM); if (supportRim == null) { - /* - Either the logFile does not have a corresponding support RIM in the backend + /* Either the logFile does not have a corresponding support RIM in the backend, or it was deleted. The support RIM for a replacement base RIM is handled - in the previous loop block. - */ + in the previous loop block. */ final SupportReferenceManifest replacementSupportRIM = new SupportReferenceManifest(String.format("%s.rimel", defaultClientName), logFile.toByteArray()); @@ -378,8 +413,8 @@ private void updateSupportRIMSUsingDeviceInfo(final String defaultClientName, new TCGEventLog(replacementSupportRIM.getRimBytes()); // no issues, continue - replacementSupportRIM.setPlatformManufacturer(deviceInfoProto.getHw().getManufacturer()); - replacementSupportRIM.setPlatformModel(deviceInfoProto.getHw().getProductName()); + replacementSupportRIM.setPlatformManufacturer(tpmDeviceInfo.getHw().getManufacturer()); + replacementSupportRIM.setPlatformModel(tpmDeviceInfo.getHw().getProductName()); replacementSupportRIM.setFileName(String.format("%s_[%s].rimel", defaultClientName, replacementSupportRIM.getHexDecHash().substring( replacementSupportRIM.getHexDecHash().length() - NUM_OF_VARIABLES))); @@ -418,11 +453,18 @@ private void updateSupportRIMSUsingDeviceInfo(final String defaultClientName, } } - private void updateAllBaseAndSupportRIMS(ProvisionerTpm2.DeviceInfo deviceInfoProto) { + /** + * Helper method that updates both base and support RIMs after modifying each type of RIM, ensuring that the + * updates are consistent and aligned. + * + * @param tpmDeviceInfo TPM Device Info + */ + private void updateBaseSupportRIMSUsingTpmDeviceInfo(ProvisionerTpm2.DeviceInfo tpmDeviceInfo) { + final String SUPPORT_RIM_FILE_PATTERN = "(\\S+(\\.(?i)(rimpcr|rimel|bin|log))$)"; final Pattern supportRimPattern = Pattern.compile(SUPPORT_RIM_FILE_PATTERN); //update Support RIMs and Base RIMs. - for (ByteString swidFile : deviceInfoProto.getSwidfileList()) { + for (ByteString swidFile : tpmDeviceInfo.getSwidfileList()) { final String swidFileHash = Base64.getEncoder().encodeToString(messageDigest.digest(swidFile.toByteArray())); @@ -476,6 +518,7 @@ private void generateDigestRecords(final String manufacturer, final String model SupportReferenceManifest baseSupportRim = null; List supplementalRims = new ArrayList<>(); List patchRims = new ArrayList<>(); + List dbSupportRims = this.referenceManifestRepository .getSupportByManufacturerModel(manufacturer, model); List expectedValues = referenceDigestValueRepository @@ -566,20 +609,23 @@ private void generateDigestRecords(final String manufacturer, final String model } } - private void updateSupportRIMSUsingEventLogAndDeviceInfo(ProvisionerTpm2.DeviceInfo deviceInfoProto) { - log.info("Device sent bios measurement log..."); + /** + * @param tpmDeviceInfo TPM Device Info + */ + private void updateEventLogInfoUsingTPMDeviceInfo(ProvisionerTpm2.DeviceInfo tpmDeviceInfo) { + final String deviceHostName = tpmDeviceInfo.getNw().getHostname(); + final String fileName = String.format("%s.measurement", deviceHostName); - final String deviceInfoHostName = deviceInfoProto.getNw().getHostname(); + log.info("Device {} sent bios measurement log...", deviceHostName); - final String fileName = String.format("%s.measurement", deviceInfoHostName); try { - // grab the event log from protobuf's device info - EventLogMeasurements deviceInfoProtoEventLog = new EventLogMeasurements(fileName, - deviceInfoProto.getLivelog().toByteArray()); + // grab the event log from TPM's device info + EventLogMeasurements tpmEventLog = new EventLogMeasurements(fileName, + tpmDeviceInfo.getLivelog().toByteArray()); // find the previous event log that's stored in the database. EventLogMeasurements integrityMeasurements = - referenceManifestRepository.byMeasurementDeviceNameUnarchived(deviceInfoHostName); + referenceManifestRepository.byMeasurementDeviceNameUnarchived(deviceHostName); // if the event log does exist in the database if (integrityMeasurements != null) { @@ -589,13 +635,13 @@ private void updateSupportRIMSUsingEventLogAndDeviceInfo(ProvisionerTpm2.DeviceI } List baseRims = referenceManifestRepository.getBaseByManufacturerModel( - deviceInfoProto.getHw().getManufacturer(), - deviceInfoProto.getHw().getProductName()); + tpmDeviceInfo.getHw().getManufacturer(), + tpmDeviceInfo.getHw().getProductName()); - deviceInfoProtoEventLog.setDeviceName(deviceInfoHostName); - deviceInfoProtoEventLog.setPlatformManufacturer(deviceInfoProto.getHw().getManufacturer()); - deviceInfoProtoEventLog.setPlatformModel(deviceInfoProto.getHw().getProductName()); - this.referenceManifestRepository.save(deviceInfoProtoEventLog); + tpmEventLog.setDeviceName(deviceHostName); + tpmEventLog.setPlatformManufacturer(tpmDeviceInfo.getHw().getManufacturer()); + tpmEventLog.setPlatformModel(tpmDeviceInfo.getHw().getProductName()); + this.referenceManifestRepository.save(tpmEventLog); for (BaseReferenceManifest bRim : baseRims) { if (bRim != null) { @@ -604,8 +650,8 @@ private void updateSupportRIMSUsingEventLogAndDeviceInfo(ProvisionerTpm2.DeviceI SupportReferenceManifest sBaseRim = referenceManifestRepository .getSupportRimEntityById(bRim.getAssociatedRim()); if (sBaseRim != null) { - bRim.setEventLogHash(deviceInfoProtoEventLog.getHexDecHash()); - sBaseRim.setEventLogHash(deviceInfoProtoEventLog.getHexDecHash()); + bRim.setEventLogHash(tpmEventLog.getHexDecHash()); + sBaseRim.setEventLogHash(tpmEventLog.getHexDecHash()); referenceManifestRepository.save(bRim); referenceManifestRepository.save(sBaseRim); } else { @@ -617,4 +663,35 @@ private void updateSupportRIMSUsingEventLogAndDeviceInfo(ProvisionerTpm2.DeviceI log.error(exception); } } + + /** + * Helper method that attempts to find all the provided device's components and stores them in the database. + * + * @param hostName device's host name + * @param paccorString string representation of the PACCOR tool output + */ + private void processDeviceComponents(final String hostName, final String paccorString) { + Map componentInfoMap = new HashMap<>(); + + try { + List componentInfos = + SupplyChainCredentialValidator.getComponentInfoFromPaccorOutput(hostName, paccorString); + + // check the DB for like component infos + List dbComponentInfos = this.componentInfoRepository.findByDeviceName(hostName); + dbComponentInfos.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"); + } + } } diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/service/IdentityClaimProcessorService.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/service/IdentityClaimProcessorService.java index 51ff31761..efca79e55 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/service/IdentityClaimProcessorService.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/service/IdentityClaimProcessorService.java @@ -4,26 +4,18 @@ import com.google.protobuf.InvalidProtocolBufferException; import com.google.protobuf.util.JsonFormat; 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.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.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.certificate.attributes.V2.ComponentIdentifierV2; -import hirs.attestationca.persist.entity.userdefined.info.ComponentInfo; import hirs.attestationca.persist.enums.AppraisalStatus; import hirs.attestationca.persist.provision.helper.ProvisionUtils; -import hirs.attestationca.persist.validation.SupplyChainCredentialValidator; import hirs.utils.HexUtils; import lombok.extern.log4j.Log4j2; import org.apache.commons.lang3.ArrayUtils; @@ -32,10 +24,7 @@ import java.io.IOException; import java.security.PublicKey; -import java.util.HashMap; -import java.util.LinkedList; import java.util.List; -import java.util.Map; @Service @Log4j2 @@ -47,10 +36,8 @@ public class IdentityClaimProcessorService { private static final String PCR_QUOTE_MASK = "0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23"; private final SupplyChainValidationService supplyChainValidationService; - private final CredentialManagementService credentialManagementService; + private final CertificateManagementService certificateManagementService; private final DeviceInfoProcessorService deviceInfoProcessorService; - private final CertificateRepository certificateRepository; - private final ComponentResultRepository componentResultRepository; private final ComponentInfoRepository componentInfoRepository; private final DeviceRepository deviceRepository; private final PolicyRepository policyRepository; @@ -60,8 +47,6 @@ public class IdentityClaimProcessorService { * Constructor. * * @param supplyChainValidationService supply chain validation service - * @param certificateRepository certificate repository - * @param componentResultRepository component result repository * @param componentInfoRepository component info repository * @param deviceRepository device repository * @param tpm2ProvisionerStateRepository tpm2 provisioner state repository @@ -70,17 +55,13 @@ public class IdentityClaimProcessorService { @Autowired public IdentityClaimProcessorService( final SupplyChainValidationService supplyChainValidationService, - final CredentialManagementService credentialManagementService, - final CertificateRepository certificateRepository, - final ComponentResultRepository componentResultRepository, + final CertificateManagementService certificateManagementService, final ComponentInfoRepository componentInfoRepository, final DeviceRepository deviceRepository, DeviceInfoProcessorService deviceInfoProcessorService, final TPM2ProvisionerStateRepository tpm2ProvisionerStateRepository, final PolicyRepository policyRepository) { this.supplyChainValidationService = supplyChainValidationService; - this.credentialManagementService = credentialManagementService; - this.certificateRepository = certificateRepository; - this.componentResultRepository = componentResultRepository; + this.certificateManagementService = certificateManagementService; this.componentInfoRepository = componentInfoRepository; this.deviceRepository = deviceRepository; this.deviceInfoProcessorService = deviceInfoProcessorService; @@ -238,49 +219,17 @@ private AppraisalStatus.Status doSupplyChainValidation(final ProvisionerTpm2.Ide // attempt to find an endorsement credential to validate EndorsementCredential endorsementCredential = - credentialManagementService.parseEcFromIdentityClaim(claim, ekPub); + certificateManagementService.parseEcFromIdentityClaim(claim, ekPub); // attempt to find platform credentials to validate - List platformCredentials = credentialManagementService.parsePcsFromIdentityClaim(claim, + List platformCredentials = certificateManagementService.parsePcsFromIdentityClaim(claim, endorsementCredential); // Parse and save device info Device device = deviceInfoProcessorService.processDeviceInfo(claim); - - 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 - // additional information from the DB if information exists - if (platformCredentials.size() == 1) { - List tempList = new LinkedList<>(); - for (PlatformCredential pc : platformCredentials) { - if (pc != null && pc.getPlatformSerial() != null) { - tempList.addAll(certificateRepository.byBoardSerialNumber(pc.getPlatformSerial())); - } - } - platformCredentials.addAll(tempList); - } - - // store component results objects - for (PlatformCredential platformCredential : platformCredentials) { - List componentResults = - componentResultRepository.findByCertificateSerialNumberAndBoardSerialNumber( - platformCredential.getSerialNumber().toString(), - platformCredential.getPlatformSerial()); - - if (componentResults.isEmpty()) { - savePlatformComponents(platformCredential); - } else { - componentResults.forEach((componentResult) -> { - componentResult.restore(); - componentResult.resetCreateTime(); - componentResultRepository.save(componentResult); - }); - } - } + // Store the platform certificates' components + certificateManagementService.saveOrUpdatePlatformCertificateComponents(platformCredentials); // perform supply chain validation SupplyChainValidationSummary summary = supplyChainValidationService.validateSupplyChain( @@ -295,78 +244,4 @@ private AppraisalStatus.Status doSupplyChainValidation(final ProvisionerTpm2.Ide return validationResult; } - - - /** - * Helper method that saves the provided platform certificate's components in the database. - * - * @param certificate certificate - */ - private void savePlatformComponents(final Certificate certificate) throws IOException { - PlatformCredential platformCredential; - - if (certificate instanceof PlatformCredential) { - platformCredential = (PlatformCredential) certificate; - ComponentResult componentResult; - - if (platformCredential.getPlatformConfigurationV1() != null) { - List componentIdentifiers = platformCredential - .getComponentIdentifiers(); - - for (ComponentIdentifier componentIdentifier : componentIdentifiers) { - componentResult = new ComponentResult(platformCredential.getPlatformSerial(), - platformCredential.getSerialNumber().toString(), - platformCredential.getPlatformChainType(), - componentIdentifier); - componentResult.setFailedValidation(false); - componentResult.setDelta(!platformCredential.isPlatformBase()); - componentResultRepository.save(componentResult); - } - } else if (platformCredential.getPlatformConfigurationV2() != null) { - List componentIdentifiersV2 = platformCredential - .getComponentIdentifiersV2(); - - for (ComponentIdentifierV2 componentIdentifierV2 : componentIdentifiersV2) { - componentResult = new ComponentResult(platformCredential.getPlatformSerial(), - platformCredential.getSerialNumber().toString(), - platformCredential.getPlatformChainType(), - componentIdentifierV2); - componentResult.setFailedValidation(false); - componentResult.setDelta(!platformCredential.isPlatformBase()); - componentResultRepository.save(componentResult); - } - } - } - } - - /** - * Helper method that attempts to find all the provided device's components. - * - * @param hostName device's host name - * @param paccorString string representation of the paccor tool output - */ - private void handleDeviceComponents(final String hostName, final String paccorString) { - Map componentInfoMap = new HashMap<>(); - - try { - List componentInfos = SupplyChainCredentialValidator - .getComponentInfoFromPaccorOutput(hostName, paccorString); - - // check the DB for like component infos - List dbComponentInfos = this.componentInfoRepository.findByDeviceName(hostName); - dbComponentInfos.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"); - } - } } diff --git a/HIRS_AttestationCA/src/test/java/hirs/attestationca/persist/provision/helper/CredentialManagementServiceTest.java b/HIRS_AttestationCA/src/test/java/hirs/attestationca/persist/provision/helper/CertificateManagementServiceTest.java similarity index 76% rename from HIRS_AttestationCA/src/test/java/hirs/attestationca/persist/provision/helper/CredentialManagementServiceTest.java rename to HIRS_AttestationCA/src/test/java/hirs/attestationca/persist/provision/helper/CertificateManagementServiceTest.java index f9f7c3133..80973b687 100644 --- a/HIRS_AttestationCA/src/test/java/hirs/attestationca/persist/provision/helper/CredentialManagementServiceTest.java +++ b/HIRS_AttestationCA/src/test/java/hirs/attestationca/persist/provision/helper/CertificateManagementServiceTest.java @@ -2,7 +2,7 @@ import hirs.attestationca.persist.entity.manager.CertificateRepository; import hirs.attestationca.persist.entity.userdefined.Certificate; -import hirs.attestationca.persist.provision.service.CredentialManagementService; +import hirs.attestationca.persist.provision.service.CertificateManagementService; import org.apache.commons.io.IOUtils; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -20,16 +20,16 @@ import static org.mockito.Mockito.verify; /** - * Unit tests for {@see CredentialManagementService}. + * Unit tests for {@see CertificateManagementService}. */ -public class CredentialManagementServiceTest { +public class CertificateManagementServiceTest { private static final String EK_HEADER_TRUNCATED = "/certificates/nuc-1/ek_cert_7_byte_header_removed.cer"; private static final String EK_UNTOUCHED = "/certificates/nuc-1/ek_cert_untouched.cer"; @InjectMocks - private CredentialManagementService credentialManagementService; + private CertificateManagementService certificateManagementService; @Mock private CertificateRepository certificateRepository; @@ -65,7 +65,7 @@ public void tearDown() throws Exception { @Test public void processNullEndorsementCredential() { assertThrows(IllegalArgumentException.class, () -> - credentialManagementService.storeEndorsementCredential(null, + certificateManagementService.storeEndorsementCredential(null, "testName")); } @@ -75,7 +75,7 @@ public void processNullEndorsementCredential() { @Test public void processEmptyEndorsementCredential() { assertThrows(IllegalArgumentException.class, () -> - credentialManagementService.storeEndorsementCredential(new byte[0], "testName")); + certificateManagementService.storeEndorsementCredential(new byte[0], "testName")); } /** @@ -85,7 +85,7 @@ public void processEmptyEndorsementCredential() { public void processInvalidEndorsementCredentialCase1() { byte[] ekBytes = new byte[]{1}; assertThrows(IllegalArgumentException.class, () -> - credentialManagementService.storeEndorsementCredential(ekBytes, "testName")); + certificateManagementService.storeEndorsementCredential(ekBytes, "testName")); } /** @@ -95,7 +95,7 @@ public void processInvalidEndorsementCredentialCase1() { public void processInvalidEndorsementCredentialCase2() { byte[] ekBytes = new byte[]{1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0}; assertThrows(IllegalArgumentException.class, () -> - credentialManagementService.storeEndorsementCredential(ekBytes, + certificateManagementService.storeEndorsementCredential(ekBytes, "testName")); } @@ -106,10 +106,11 @@ public void processInvalidEndorsementCredentialCase2() { */ @Test public void parseUntouchedEndorsementCredential() throws IOException { - String path = Objects.requireNonNull(CredentialManagementServiceTest.class.getResource(EK_UNTOUCHED)).getPath(); + String path = + Objects.requireNonNull(CertificateManagementServiceTest.class.getResource(EK_UNTOUCHED)).getPath(); byte[] ekBytes = IOUtils.toByteArray(new FileInputStream(path)); - credentialManagementService.storeEndorsementCredential(ekBytes, "testName"); + certificateManagementService.storeEndorsementCredential(ekBytes, "testName"); verify(certificateRepository).save(any(Certificate.class)); } @@ -120,11 +121,11 @@ public void parseUntouchedEndorsementCredential() throws IOException { */ @Test public void parseHeaderTruncatedEndorsementCredential() throws IOException { - String path = Objects.requireNonNull(CredentialManagementServiceTest.class.getResource(EK_HEADER_TRUNCATED)) + String path = Objects.requireNonNull(CertificateManagementServiceTest.class.getResource(EK_HEADER_TRUNCATED)) .getPath(); byte[] ekBytes = IOUtils.toByteArray(new FileInputStream(path)); - credentialManagementService.storeEndorsementCredential(ekBytes, "testName"); + certificateManagementService.storeEndorsementCredential(ekBytes, "testName"); verify(certificateRepository).save(any(Certificate.class)); } } From a19068280fb1e92cd3c8b92f480db311c062d22d Mon Sep 17 00:00:00 2001 From: ThatSilentCoder <184309164+ThatSilentCoder@users.noreply.github.com> Date: Fri, 6 Mar 2026 16:12:36 -0500 Subject: [PATCH 13/25] v3.1_issue_1090: Fixed the error that is mentioned in the issue. Will need to add algo param to the powershell scripts so this works on windows and will have to address scenarios where the ak and ek keys are not rsa keys. currently have a method that handles that situation but will need to figure out how to implement that issue. --- .../SupplyChainValidationRepository.java | 2 +- .../persist/enums/PublicKeyAlgorithm.java | 19 +- ...ionCertificateAuthorityRestController.java | 4 +- .../provision/helper/ProvisionUtils.java | 70 ++-- ...tationCertificateAuthorityServiceImpl.java | 2 +- .../CertificateRequestProcessorService.java | 268 +++++++------- ....java => CredentialManagementService.java} | 20 +- .../service/DeviceInfoProcessorService.java | 339 +++++++++--------- .../IdentityClaimProcessorService.java | 96 ++--- .../provision/service/package-info.java | 1 + ...tationCertificateAuthorityServiceTest.java | 6 +- ...a => CredentialManagementServiceTest.java} | 22 +- .../resources/{public_keys => tpm2}/ak.name | Bin .../resources/{public_keys => tpm2}/ak.pub | Bin .../resources/{public_keys => tpm2}/ek.pub | Bin 15 files changed, 434 insertions(+), 415 deletions(-) rename HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/service/{CertificateManagementService.java => CredentialManagementService.java} (95%) create mode 100644 HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/service/package-info.java rename HIRS_AttestationCA/src/test/java/hirs/attestationca/persist/provision/helper/{CertificateManagementServiceTest.java => CredentialManagementServiceTest.java} (78%) rename HIRS_AttestationCA/src/test/resources/{public_keys => tpm2}/ak.name (100%) rename HIRS_AttestationCA/src/test/resources/{public_keys => tpm2}/ak.pub (100%) rename HIRS_AttestationCA/src/test/resources/{public_keys => tpm2}/ek.pub (100%) diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/manager/SupplyChainValidationRepository.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/manager/SupplyChainValidationRepository.java index e9446c0f6..b7571b423 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/manager/SupplyChainValidationRepository.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/entity/manager/SupplyChainValidationRepository.java @@ -25,4 +25,4 @@ public interface SupplyChainValidationRepository extends JpaRepository findByValidationResult(AppraisalStatus.Status validationResult); -} \ No newline at end of file +} diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/enums/PublicKeyAlgorithm.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/enums/PublicKeyAlgorithm.java index 243e56fe2..4e43467db 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/enums/PublicKeyAlgorithm.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/enums/PublicKeyAlgorithm.java @@ -38,7 +38,7 @@ public enum PublicKeyAlgorithm { UNKNOWN(0xFFFF, "UNKNOWN"); /** - * The hexadecimal representation of the algorithm ID. + * The hexadecimal representation of the algorithm ID as represented in the TCG documents. */ private final int algorithmId; @@ -46,22 +46,7 @@ public enum PublicKeyAlgorithm { * The name of the algorithm. */ private final String algorithmName; - - /** - * Converts the provided string public key algorithm into an ENUM. - * - * @param algorithmAsString public key algorithm name as a string - * @return ENUM representation of the public key algorithm - */ - public static PublicKeyAlgorithm fromString(final String algorithmAsString) { - for (PublicKeyAlgorithm algorithmEnum : PublicKeyAlgorithm.values()) { - if (algorithmEnum.getAlgorithmName().equalsIgnoreCase(algorithmAsString)) { - return algorithmEnum; - } - } - return UNKNOWN; // Return UNKNOWN if no match is found - } - + /** * Retrieves the enum by the algorithm ID. * diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/AttestationCertificateAuthorityRestController.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/AttestationCertificateAuthorityRestController.java index 0c0e728e1..346df7117 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/AttestationCertificateAuthorityRestController.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/AttestationCertificateAuthorityRestController.java @@ -59,9 +59,9 @@ public byte[] processCertificateRequest(@RequestBody final byte[] certificateReq } /** - * Processes a GET request to retrieve the byte array representation of the public key. + * Processes a GET request to retrieve the byte array representation of the leaf ACA certificate's public key. * - * @return byte array representation of the public key + * @return byte array representation of the leaf ACA certificate's public key */ @ResponseBody @GetMapping("/public-key") diff --git a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/helper/ProvisionUtils.java b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/helper/ProvisionUtils.java index 824066124..84dc43022 100644 --- a/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/helper/ProvisionUtils.java +++ b/HIRS_AttestationCA/src/main/java/hirs/attestationca/persist/provision/helper/ProvisionUtils.java @@ -185,12 +185,15 @@ public static PublicKeyAlgorithm determinePublicKeyAlgorithm(byte[] byteArray) { return PublicKeyAlgorithm.UNKNOWN; } + final int unsignedByteMask = 0xFF; + final int byteShiftVal = 8; + // Extract the first two bytes from the byte array and combine them into a 16-bit integer (algorithm ID). // - `byteArray[0] & 0xFF` ensures the first byte is treated as unsigned (0-255 range). // - `<< 8` shifts the first byte 8 bits to the left, placing it in the higher byte of the 16-bit value. // - `byteArray[1] & 0xFF` ensures the second byte is treated as unsigned, then it occupies the lower byte. // - The bitwise OR (`|`) combines the two parts into a single 16-bit value (int). - final int algorithmId = ((byteArray[0] & 0xFF) << 8) | (byteArray[1] & 0xFF); + final int algorithmId = ((byteArray[0] & unsignedByteMask) << byteShiftVal) | (byteArray[1] & unsignedByteMask); // Compare the extracted ID with the enums return PublicKeyAlgorithm.fromId(algorithmId); @@ -250,7 +253,6 @@ public static RSAPublicKey assembleRSAPublicKey(final BigInteger modulus) { } /** - * todo * Parses the ECC public key from public data segment generated by TPM 2.0. * * @param publicDataSegment the public area segment to parse @@ -273,10 +275,12 @@ public static ECPublicKey parseECCKeyFromPublicDataSegment(final byte[] publicDa } /** - * todo + * Assembles the ECC public key using the provided byte array. + * + * @param eccKeySection ecc public key section of the byte array + * @return the ECC public key */ public static ECPublicKey assembleECCPublicKey(final byte[] eccKeySection) { - // create the ECC public key try { KeyFactory keyFactory = KeyFactory.getInstance("EC"); @@ -292,33 +296,34 @@ public static ECPublicKey assembleECCPublicKey(final byte[] eccKeySection) { } /** - * @param endorsementPublicKey endorsement key in the identity claim - * @param attestationPublicKey attestation key in the identity claim + * Determines the public key algorithm of both the endorsement and attestation public key in order to + * properly create a credential blob string. + * + * @param endorsementPublicKey endorsement public key in the identity claim + * @param attestationPublicKey attestation public key in the identity claim * @param secret a nonce * @return the encrypted blob forming the identity claim challenge */ - public static ByteString tpm20MakeCredential( - final PublicKey endorsementPublicKey, - final PublicKey attestationPublicKey, - final byte[] secret) { - - final PublicKeyAlgorithm publicKeyAlgorithm = PublicKeyAlgorithm.RSA; - + public static ByteString tpm20MakeCredential(final PublicKey endorsementPublicKey, + final PublicKey attestationPublicKey, + final byte[] secret) { // check size of the secret if (secret.length > MAX_SECRET_LENGTH) { - throw new IllegalArgumentException("Secret must be " + MAX_SECRET_LENGTH - + " bytes or smaller."); + throw new IllegalArgumentException("Secret must be " + MAX_SECRET_LENGTH + " bytes or smaller."); } - return switch (publicKeyAlgorithm) { - case RSA -> tpm20MakeCredentialUsingRSA( + if (endorsementPublicKey instanceof RSAPublicKey && attestationPublicKey instanceof RSAPublicKey) { + return tpm20MakeCredentialUsingRSA( (RSAPublicKey) endorsementPublicKey, (RSAPublicKey) attestationPublicKey, secret); - case ECC -> tpm20MakeCredentialUsingECC( + } else if (endorsementPublicKey instanceof ECPublicKey && attestationPublicKey instanceof ECPublicKey) { + return tpm20MakeCredentialUsingECC( (ECPublicKey) endorsementPublicKey, (ECPublicKey) attestationPublicKey, secret); - default -> throw new UnsupportedOperationException("Unsupported or invalid public key algorithm"); - }; + } + + throw new UnsupportedOperationException("Mismatched or unsupported public key algorithms: " + + endorsementPublicKey.getAlgorithm() + " / " + attestationPublicKey.getAlgorithm()); } /** @@ -351,9 +356,10 @@ public static ByteString tpm20MakeCredentialUsingRSA(final RSAPublicKey endorsem try { // encrypt seed with endorsement RSA Public Key Cipher asymCipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding"); + OAEPParameterSpec oaepSpec = new OAEPParameterSpec("SHA-256", "MGF1", - MGF1ParameterSpec.SHA256, - new PSource.PSpecified("IDENTITY\0".getBytes(StandardCharsets.UTF_8))); + MGF1ParameterSpec.SHA256, new PSource.PSpecified("IDENTITY\0".getBytes(StandardCharsets.UTF_8))); + asymCipher.init(Cipher.PUBLIC_KEY, endorsementRSAKey, oaepSpec); asymCipher.update(seed); byte[] encSeed = asymCipher.doFinal(); @@ -417,6 +423,7 @@ public static ByteString tpm20MakeCredentialUsingRSA(final RSAPublicKey endorsem } /** + * todo * Performs the first step of the TPM 2.0 identity claim process using the ECC public key algorithm: *