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