diff --git a/pom.xml b/pom.xml index eeb5e064..200c8aad 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ com.uid2 uid2-shared - 8.0.32 + 8.0.34-alpha-191-SNAPSHOT ${project.groupId}:${project.artifactId} Library for all the shared uid2 operations https://github.com/IABTechLab/uid2docs diff --git a/src/main/java/com/uid2/shared/secure/AzureCCCoreAttestationService.java b/src/main/java/com/uid2/shared/secure/AzureCCCoreAttestationService.java index ae44ef79..417f5e32 100644 --- a/src/main/java/com/uid2/shared/secure/AzureCCCoreAttestationService.java +++ b/src/main/java/com/uid2/shared/secure/AzureCCCoreAttestationService.java @@ -25,14 +25,17 @@ public class AzureCCCoreAttestationService implements ICoreAttestationService { private final IPolicyValidator policyValidator; - public AzureCCCoreAttestationService(String maaServerBaseUrl, String attestationUrl) { - this(new MaaTokenSignatureValidator(maaServerBaseUrl), new PolicyValidator(attestationUrl)); + private final String azureCcProtocol; + + public AzureCCCoreAttestationService(String maaServerBaseUrl, String attestationUrl, String azureCcProtocol) { + this(new MaaTokenSignatureValidator(maaServerBaseUrl), new PolicyValidator(attestationUrl), azureCcProtocol); } // used in UT - protected AzureCCCoreAttestationService(IMaaTokenSignatureValidator tokenSignatureValidator, IPolicyValidator policyValidator) { + protected AzureCCCoreAttestationService(IMaaTokenSignatureValidator tokenSignatureValidator, IPolicyValidator policyValidator, String azureCcProtocol) { this.tokenSignatureValidator = tokenSignatureValidator; this.policyValidator = policyValidator; + this.azureCcProtocol = azureCcProtocol; } @Override @@ -40,8 +43,9 @@ public void attest(byte[] attestationRequest, byte[] publicKey, Handler { assertTrue(ar.succeeded()); @@ -69,11 +75,12 @@ public void testHappyPath() throws AttestationException { }); } - @Test - public void testSignatureCheckFailed_ClientError() throws AttestationException { + @ParameterizedTest + @MethodSource("argumentProvider") + public void testSignatureCheckFailed_ClientError(String azureProtocol) throws AttestationException { var errorStr = "token signature validation failed"; - when(alwaysFailTokenValidator.validate(any())).thenThrow(new AttestationClientException(errorStr, AttestationFailure.BAD_PAYLOAD)); - var provider = new AzureCCCoreAttestationService(alwaysFailTokenValidator, alwaysPassPolicyValidator); + when(alwaysFailTokenValidator.validate(any(), any())).thenThrow(new AttestationClientException(errorStr, AttestationFailure.BAD_PAYLOAD)); + var provider = new AzureCCCoreAttestationService(alwaysFailTokenValidator, alwaysPassPolicyValidator, azureProtocol); provider.registerEnclave(ENCLAVE_ID); attest(provider, ar -> { assertTrue(ar.succeeded()); @@ -82,10 +89,11 @@ public void testSignatureCheckFailed_ClientError() throws AttestationException { }); } - @Test - public void testSignatureCheckFailed_ServerError() throws AttestationException { - when(alwaysFailTokenValidator.validate(any())).thenThrow(new AttestationException("unknown server error")); - var provider = new AzureCCCoreAttestationService(alwaysFailTokenValidator, alwaysPassPolicyValidator); + @ParameterizedTest + @MethodSource("argumentProvider") + public void testSignatureCheckFailed_ServerError(String azureProtocol) throws AttestationException { + when(alwaysFailTokenValidator.validate(any(), any())).thenThrow(new AttestationException("unknown server error")); + var provider = new AzureCCCoreAttestationService(alwaysFailTokenValidator, alwaysPassPolicyValidator, azureProtocol); provider.registerEnclave(ENCLAVE_ID); attest(provider, ar -> { assertFalse(ar.succeeded()); @@ -93,11 +101,12 @@ public void testSignatureCheckFailed_ServerError() throws AttestationException { }); } - @Test - public void testPolicyCheckSuccess_ClientError() throws AttestationException { + @ParameterizedTest + @MethodSource("argumentProvider") + public void testPolicyCheckSuccess_ClientError(String azureProtocol) throws AttestationException { var errorStr = "policy validation failed"; when(alwaysFailPolicyValidator.validate(any(), any())).thenThrow(new AttestationClientException(errorStr, AttestationFailure.BAD_PAYLOAD)); - var provider = new AzureCCCoreAttestationService(alwaysFailTokenValidator, alwaysFailPolicyValidator); + var provider = new AzureCCCoreAttestationService(alwaysFailTokenValidator, alwaysFailPolicyValidator, azureProtocol); provider.registerEnclave(ENCLAVE_ID); attest(provider, ar -> { assertTrue(ar.succeeded()); @@ -106,10 +115,11 @@ public void testPolicyCheckSuccess_ClientError() throws AttestationException { }); } - @Test - public void testPolicyCheckFailed_ServerError() throws AttestationException { + @ParameterizedTest + @MethodSource("argumentProvider") + public void testPolicyCheckFailed_ServerError(String azureProtocol) throws AttestationException { when(alwaysFailPolicyValidator.validate(any(), any())).thenThrow(new AttestationException("unknown server error")); - var provider = new AzureCCCoreAttestationService(alwaysFailTokenValidator, alwaysFailPolicyValidator); + var provider = new AzureCCCoreAttestationService(alwaysFailTokenValidator, alwaysFailPolicyValidator, azureProtocol); provider.registerEnclave(ENCLAVE_ID); attest(provider, ar -> { assertFalse(ar.succeeded()); @@ -117,9 +127,10 @@ public void testPolicyCheckFailed_ServerError() throws AttestationException { }); } - @Test - public void testEnclaveNotRegistered() throws AttestationException { - var provider = new AzureCCCoreAttestationService(alwaysFailTokenValidator, alwaysPassPolicyValidator); + @ParameterizedTest + @MethodSource("argumentProvider") + public void testEnclaveNotRegistered(String azureProtocol) throws AttestationException { + var provider = new AzureCCCoreAttestationService(alwaysFailTokenValidator, alwaysPassPolicyValidator, azureProtocol); attest(provider, ar -> { assertTrue(ar.succeeded()); assertFalse(ar.result().isSuccess()); @@ -133,4 +144,11 @@ private static void attest(ICoreAttestationService provider, Handler argumentProvider() { + return Stream.of( + Arguments.of(MaaTokenPayload.AZURE_CC_ACI_PROTOCOL), + Arguments.of(MaaTokenPayload.AZURE_CC_AKS_PROTOCOL) + ); + } } diff --git a/src/test/java/com/uid2/shared/secure/azurecc/MaaTokenSignatureValidatorTest.java b/src/test/java/com/uid2/shared/secure/azurecc/MaaTokenSignatureValidatorTest.java index a5d9257f..e932d1f5 100644 --- a/src/test/java/com/uid2/shared/secure/azurecc/MaaTokenSignatureValidatorTest.java +++ b/src/test/java/com/uid2/shared/secure/azurecc/MaaTokenSignatureValidatorTest.java @@ -3,17 +3,21 @@ import com.uid2.shared.secure.AttestationException; import com.uid2.shared.secure.TestClock; import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.stream.Stream; import static com.uid2.shared.secure.TestUtils.loadFromJson; import static com.uid2.shared.secure.azurecc.MaaTokenUtils.validateAndParseToken; import static org.junit.jupiter.api.Assertions.assertEquals; public class MaaTokenSignatureValidatorTest { - @Test - public void testPayload() throws Exception { + @ParameterizedTest + @MethodSource("argumentProvider") + public void testPayload(String payloadPath, String protocol) throws Exception { // expire at 1695313895 - var payloadPath = "/com.uid2.shared/test/secure/azurecc/jwt_payload.json"; var payload = loadFromJson(payloadPath); var clock = new TestClock(); clock.setCurrentTimeMs(1695313893000L); @@ -22,7 +26,7 @@ public void testPayload() throws Exception { var expectedLocation = "East US"; var expectedPublicKey = "abc"; - var tokenPayload = validateAndParseToken(payload, clock); + var tokenPayload = validateAndParseToken(payload, clock, protocol); assertEquals(true, tokenPayload.isSevSnpVM()); assertEquals(true, tokenPayload.isUtilityVMCompliant()); assertEquals(false, tokenPayload.isVmDebuggable()); @@ -37,6 +41,13 @@ public void testE2E() throws AttestationException { var maaToken = ""; var maaServerUrl = "https://sharedeus.eus.attest.azure.net"; var validator = new MaaTokenSignatureValidator(maaServerUrl); - var token = validator.validate(maaToken); + var token = validator.validate(maaToken, MaaTokenPayload.AZURE_CC_ACI_PROTOCOL); + } + + static Stream argumentProvider() { + return Stream.of( + Arguments.of("/com.uid2.shared/test/secure/azurecc/jwt_payload_aci.json", MaaTokenPayload.AZURE_CC_ACI_PROTOCOL), + Arguments.of("/com.uid2.shared/test/secure/azurecc/jwt_payload_aks.json", MaaTokenPayload.AZURE_CC_AKS_PROTOCOL) + ); } } diff --git a/src/test/java/com/uid2/shared/secure/azurecc/MaaTokenUtils.java b/src/test/java/com/uid2/shared/secure/azurecc/MaaTokenUtils.java index 48d1335e..82714b1e 100644 --- a/src/test/java/com/uid2/shared/secure/azurecc/MaaTokenUtils.java +++ b/src/test/java/com/uid2/shared/secure/azurecc/MaaTokenUtils.java @@ -14,7 +14,7 @@ public class MaaTokenUtils { public static final String MAA_BASE_URL = "https://sharedeus.eus.attest.azure.net"; - public static MaaTokenPayload validateAndParseToken(JsonObject payload, Clock clock) throws Exception{ + public static MaaTokenPayload validateAndParseToken(JsonObject payload, Clock clock, String protocol) throws Exception{ var gen = KeyPairGenerator.getInstance(Const.Name.AsymetricEncryptionKeyClass); gen.initialize(2048, new SecureRandom()); var keyPair = gen.generateKeyPair(); @@ -30,7 +30,7 @@ public static MaaTokenPayload validateAndParseToken(JsonObject payload, Clock cl var tokenVerifier = new MaaTokenSignatureValidator(MAA_BASE_URL, keyProvider, clock); // validate token - return tokenVerifier.validate(token); + return tokenVerifier.validate(token, protocol); } private static class MockKeyProvider implements IPublicKeyProvider { diff --git a/src/test/java/com/uid2/shared/secure/azurecc/PolicyValidatorTest.java b/src/test/java/com/uid2/shared/secure/azurecc/PolicyValidatorTest.java index 59c3630b..9e4cde11 100644 --- a/src/test/java/com/uid2/shared/secure/azurecc/PolicyValidatorTest.java +++ b/src/test/java/com/uid2/shared/secure/azurecc/PolicyValidatorTest.java @@ -97,6 +97,7 @@ private MaaTokenPayload generateBasicPayload() { .vmDebuggable(false) .runtimeData(generateBasicRuntimeData()) .ccePolicyDigest(CCE_POLICY_DIGEST) + .azureProtocol(MaaTokenPayload.AZURE_CC_ACI_PROTOCOL) .build(); } @@ -125,4 +126,53 @@ public void testValidationFailure_DifferentAttestationUrl() { assertEquals(AttestationFailure.UNKNOWN_ATTESTATION_URL, ((AttestationClientException)t).getAttestationFailure()); } + + @Test + public void testValidationFailure_AzureCcWithOtherUvm() { + var validator = new PolicyValidator(ATTESTATION_URL); + var aksPayload = generateBasicPayload() + .toBuilder() + .complianceStatus("fake-compliance") + .build(); + Throwable t = assertThrows(AttestationException.class, ()-> validator.validate(aksPayload, PUBLIC_KEY)); + assertEquals("Not run in Azure Compliance Utility VM", t.getMessage()); + assertEquals(AttestationFailure.BAD_FORMAT, ((AttestationClientException)t).getAttestationFailure()); + } + + @Test + public void testValidationSuccess_AksWithAzureSignedKataccUvm() throws AttestationClientException { + var validator = new PolicyValidator(ATTESTATION_URL); + var aksPayload = generateBasicPayload() + .toBuilder() + .complianceStatus("azure-signed-katacc-uvm") + .azureProtocol(MaaTokenPayload.AZURE_CC_AKS_PROTOCOL) + .build(); + var enclaveId = validator.validate(aksPayload, PUBLIC_KEY); + assertEquals(CCE_POLICY_DIGEST, enclaveId); + } + + @Test + public void testValidationFailure_AksWithOtherUvm() { + var validator = new PolicyValidator(ATTESTATION_URL); + var aksPayload = generateBasicPayload() + .toBuilder() + .complianceStatus("fake-compliance") + .azureProtocol(MaaTokenPayload.AZURE_CC_AKS_PROTOCOL) + .build(); + Throwable t = assertThrows(AttestationException.class, ()-> validator.validate(aksPayload, PUBLIC_KEY)); + assertEquals("Not run in Azure Compliance Utility VM", t.getMessage()); + assertEquals(AttestationFailure.BAD_FORMAT, ((AttestationClientException)t).getAttestationFailure()); + } + + @Test + public void testValidationFailure_InvalidProtocol() { + var validator = new PolicyValidator(ATTESTATION_URL); + var aksPayload = generateBasicPayload() + .toBuilder() + .azureProtocol("fake-protocol") + .build(); + Throwable t = assertThrows(AttestationException.class, ()-> validator.validate(aksPayload, PUBLIC_KEY)); + assertEquals("Azure protocol: fake-protocol not supported", t.getMessage()); + assertEquals(AttestationFailure.INVALID_PROTOCOL, ((AttestationClientException)t).getAttestationFailure()); + } } diff --git a/src/test/resources/com.uid2.shared/test/secure/azurecc/jwt_payload.json b/src/test/resources/com.uid2.shared/test/secure/azurecc/jwt_payload_aci.json similarity index 100% rename from src/test/resources/com.uid2.shared/test/secure/azurecc/jwt_payload.json rename to src/test/resources/com.uid2.shared/test/secure/azurecc/jwt_payload_aci.json diff --git a/src/test/resources/com.uid2.shared/test/secure/azurecc/jwt_payload_aks.json b/src/test/resources/com.uid2.shared/test/secure/azurecc/jwt_payload_aks.json new file mode 100644 index 00000000..4a744ee3 --- /dev/null +++ b/src/test/resources/com.uid2.shared/test/secure/azurecc/jwt_payload_aks.json @@ -0,0 +1,41 @@ +{ + "exp": 1695313895, + "iat": 1695285095, + "iss": "https://sharedeus.eus.attest.azure.net", + "jti": "3b16f2ab4492417aae4cc9a5e6506ca2519659c0d8fdc2bf442fe01aa9b8e46c", + "nbf": 1695285095, + "nonce": "7394904505194784658", + "x-ms-attestation-type": "sevsnpvm", + "x-ms-compliance-status": "azure-signed-katacc-uvm", + "x-ms-policy-hash": "9NY0VnTQ-IiBriBplVUpFbczcDaEBUwsiFYAzHu_gco", + "x-ms-runtime": { + "location": "East US", + "publicKey": "abc" + }, + "x-ms-sevsnpvm-authorkeydigest": "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "x-ms-sevsnpvm-bootloader-svn": 3, + "x-ms-sevsnpvm-familyId": "01000000000000000000000000000000", + "x-ms-sevsnpvm-guestsvn": 2, + "x-ms-sevsnpvm-hostdata": "fef932e0103f6132437e8a1223f32efc4bea63342f893b5124645224ef29ba73", + "x-ms-sevsnpvm-idkeydigest": "ebeeeabce075eeaba3d9ea24d8495137a2877c0d20ac6ea73fc6d2f8aeb50de132150e0a0752664919bcebbf2e8c5807", + "x-ms-sevsnpvm-imageId": "02000000000000000000000000000000", + "x-ms-sevsnpvm-is-debuggable": false, + "x-ms-sevsnpvm-launchmeasurement": "03fea02823189b25d0623a5c81f97c8ba4d2fbc48c914a55ce525f90454ddcec303743dac2fc013f0846912d1412f6df", + "x-ms-sevsnpvm-microcode-svn": 115, + "x-ms-sevsnpvm-migration-allowed": false, + "x-ms-sevsnpvm-reportdata": "4e7d4a413745ddea79f05d20d9ac7add3659ac783ef24684127bbbb3e50fc63c0000000000000000000000000000000000000000000000000000000000000000", + "x-ms-sevsnpvm-reportid": "d137a83c2d42d81dd42d39ad95ef9023de63216ddaaf2c368a8c41a636ddb2a9", + "x-ms-sevsnpvm-smt-allowed": true, + "x-ms-sevsnpvm-snpfw-svn": 8, + "x-ms-sevsnpvm-tee-svn": 0, + "x-ms-sevsnpvm-uvm-endorsement": { + "x-ms-sevsnpvm-guestsvn": "100", + "x-ms-sevsnpvm-launchmeasurement": "03fea02823189b25d0623a5c81f97c8ba4d2fbc48c914a55ce525f90454ddcec303743dac2fc013f0846912d1412f6df" + }, + "x-ms-sevsnpvm-uvm-endorsement-headers": { + "feed": "ContainerPlat-AMD-UVM", + "iss": "did:x509:0:sha256:I__iuL25oXEVFdTP_aBLx_eT1RPHbCQ_ECBQfYZpt9s::eku:1.3.6.1.4.1.311.76.59.1.2" + }, + "x-ms-sevsnpvm-vmpl": 0, + "x-ms-ver": "1.0" +}