Skip to content

Commit 41efc58

Browse files
cYKatherineRelease Workflow
andauthored
UID2-2799 Verify attestation url for Azure (#183)
* Add verification for attestion url * Add unit tests for verifying attestation url * Rename `attestationUrl` to `allowedAttestationUrl` * Encode and decode attestationUrl * Rename `allowedAttestationUrl` to `attestationUrl` * Pass in attestation token to the policy validator constructor * Remove slf4j * Add policy validator test * Rename final variable in gcp policy test to `ATTESTATION_URL` * Address comments * Handle new core with previous operator change * [CI Pipeline] Released Snapshot version: 7.1.13-SNAPSHOT --------- Co-authored-by: Release Workflow <[email protected]>
1 parent 924764f commit 41efc58

File tree

8 files changed

+102
-30
lines changed

8 files changed

+102
-30
lines changed

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
<groupId>com.uid2</groupId>
88
<artifactId>uid2-shared</artifactId>
9-
<version>7.1.0-8e67b3a537</version>
9+
<version>7.1.13-SNAPSHOT</version>
1010
<name>${project.groupId}:${project.artifactId}</name>
1111
<description>Library for all the shared uid2 operations</description>
1212
<url>https://github.com/IABTechLab/uid2docs</url>

src/main/java/com/uid2/shared/secure/AzureCCCoreAttestationService.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ public class AzureCCCoreAttestationService implements ICoreAttestationService {
2525

2626
private final IPolicyValidator policyValidator;
2727

28-
public AzureCCCoreAttestationService(String maaServerBaseUrl) {
29-
this(new MaaTokenSignatureValidator(maaServerBaseUrl), new PolicyValidator());
28+
public AzureCCCoreAttestationService(String maaServerBaseUrl, String attestationUrl) {
29+
this(new MaaTokenSignatureValidator(maaServerBaseUrl), new PolicyValidator(attestationUrl));
3030
}
3131

3232
// used in UT
@@ -58,8 +58,7 @@ public void attest(byte[] attestationRequest, byte[] publicKey, Handler<AsyncRes
5858
}
5959
catch (AttestationClientException ace){
6060
handler.handle(Future.succeededFuture(new AttestationResult(ace)));
61-
}
62-
catch (AttestationException ae) {
61+
} catch (AttestationException ae) {
6362
handler.handle(Future.failedFuture(ae));
6463
} catch (Exception ex) {
6564
handler.handle(Future.failedFuture(new AttestationException(ex)));

src/main/java/com/uid2/shared/secure/azurecc/MaaTokenSignatureValidator.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ public MaaTokenPayload validate(String tokenString) throws AttestationException
8686

8787
if(runtime != null){
8888
var runtimeDataBuilder = RuntimeData.builder();
89+
runtimeDataBuilder.attestationUrl(tryGetField(runtime, "attestationUrl", String.class));
8990
runtimeDataBuilder.location(tryGetField(runtime, "location", String.class));
9091
runtimeDataBuilder.publicKey(tryGetField(runtime, "publicKey", String.class));
9192
tokenPayloadBuilder.runtimeData(runtimeDataBuilder.build());

src/main/java/com/uid2/shared/secure/azurecc/PolicyValidator.java

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,22 @@
44
import com.uid2.shared.secure.AttestationClientException;
55
import com.uid2.shared.secure.AttestationException;
66
import com.uid2.shared.secure.AttestationFailure;
7+
import com.uid2.shared.util.UrlEquivalenceValidator;
78

89
public class PolicyValidator implements IPolicyValidator{
910
private static final String LOCATION_CHINA = "china";
1011
private static final String LOCATION_EU = "europe";
12+
private String attestationUrl;
13+
14+
public PolicyValidator(String attestationUrl) {
15+
this.attestationUrl = attestationUrl;
16+
}
1117
@Override
1218
public String validate(MaaTokenPayload maaTokenPayload, String publicKey) throws AttestationException {
1319
verifyVM(maaTokenPayload);
1420
verifyLocation(maaTokenPayload);
1521
verifyPublicKey(maaTokenPayload, publicKey);
22+
verifyAttestationUrl(maaTokenPayload);
1623
return maaTokenPayload.getCcePolicyDigest();
1724
}
1825

@@ -23,14 +30,23 @@ private void verifyPublicKey(MaaTokenPayload maaTokenPayload, String publicKey)
2330
var runtimePublicKey = maaTokenPayload.getRuntimeData().getPublicKey();
2431
if(!publicKey.equals(runtimePublicKey)){
2532
throw new AttestationClientException(
26-
String.format("Public key in payload is not match expected value. More info: runtime(%s), expected(%s)",
33+
String.format("Public key in payload does not match expected value. More info: runtime(%s), expected(%s)",
2734
runtimePublicKey,
2835
publicKey
2936
),
3037
AttestationFailure.BAD_FORMAT);
3138
}
3239
}
3340

41+
private void verifyAttestationUrl(MaaTokenPayload maaTokenPayload) throws AttestationException {
42+
String decodedRuntimeAttestationUrl = maaTokenPayload.getRuntimeData().getDecodedAttestationUrl();
43+
if (decodedRuntimeAttestationUrl == null) {
44+
return;
45+
} else if (!UrlEquivalenceValidator.areUrlsEquivalent(decodedRuntimeAttestationUrl, this.attestationUrl)) {
46+
throw new AttestationClientException("The given attestation URL is unknown. Given URL: " + decodedRuntimeAttestationUrl, AttestationFailure.UNKNOWN_ATTESTATION_URL);
47+
}
48+
}
49+
3450
private void verifyVM(MaaTokenPayload maaTokenPayload) throws AttestationException {
3551
if(!maaTokenPayload.isSevSnpVM()){
3652
throw new AttestationClientException("Not in SevSnp VM", AttestationFailure.BAD_FORMAT);
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,22 @@
11
package com.uid2.shared.secure.azurecc;
22

3+
import com.amazonaws.util.Base64;
34
import lombok.Builder;
45
import lombok.Value;
56

7+
import java.nio.charset.StandardCharsets;
8+
69
@Value
710
@Builder(toBuilder = true)
811
public class RuntimeData {
12+
private String attestationUrl;
913
private String location;
1014
private String publicKey;
15+
16+
public String getDecodedAttestationUrl() {
17+
if (attestationUrl != null) {
18+
return new String(Base64.decode(attestationUrl), StandardCharsets.UTF_8);
19+
}
20+
return null;
21+
}
1122
}

src/test/java/com/uid2/shared/secure/AzureCCCoreAttestationServiceTest.java

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import com.uid2.shared.secure.azurecc.IMaaTokenSignatureValidator;
44
import com.uid2.shared.secure.azurecc.IPolicyValidator;
55
import com.uid2.shared.secure.azurecc.MaaTokenPayload;
6+
import com.uid2.shared.secure.azurecc.RuntimeData;
67
import io.vertx.core.AsyncResult;
78
import io.vertx.core.Handler;
89
import org.junit.jupiter.api.BeforeEach;
@@ -13,7 +14,10 @@
1314
import org.mockito.junit.jupiter.MockitoSettings;
1415
import org.mockito.quality.Strictness;
1516

17+
import java.nio.ByteBuffer;
1618
import java.nio.charset.StandardCharsets;
19+
import java.util.Arrays;
20+
import java.util.Base64;
1721

1822
import static org.junit.jupiter.api.Assertions.*;
1923
import static org.mockito.ArgumentMatchers.any;
@@ -22,13 +26,21 @@
2226
@ExtendWith(MockitoExtension.class)
2327
@MockitoSettings(strictness = Strictness.LENIENT)
2428
class AzureCCCoreAttestationServiceTest {
29+
private static byte[] encodeStringUnicodeAttestationEndpoint(String data) {
30+
// buffer.array() may include extra empty bytes at the end. This returns only the bytes that have data
31+
ByteBuffer buffer = StandardCharsets.UTF_8.encode(data);
32+
return Arrays.copyOf(buffer.array(), buffer.limit());
33+
}
2534
private static final String ATTESTATION_REQUEST = "test-attestation-request";
2635

2736
private static final String PUBLIC_KEY = "test-public-key";
2837

2938
private static final String ENCLAVE_ID = "test-enclave";
39+
private static final String ATTESTATION_URL = "https://example.com";
40+
private static final String ENCODED_ATTESTATION_URL = Base64.getEncoder().encodeToString(encodeStringUnicodeAttestationEndpoint(ATTESTATION_URL));
41+
private static final RuntimeData RUNTIME_DATA = RuntimeData.builder().attestationUrl(ENCODED_ATTESTATION_URL).build();
3042

31-
private static final MaaTokenPayload VALID_TOKEN_PAYLOAD = MaaTokenPayload.builder().build();
43+
private static final MaaTokenPayload VALID_TOKEN_PAYLOAD = MaaTokenPayload.builder().runtimeData(RUNTIME_DATA).build();
3244
@Mock
3345
private IMaaTokenSignatureValidator alwaysPassTokenValidator;
3446

@@ -82,7 +94,7 @@ public void testSignatureCheckFailed_ServerError() throws AttestationException {
8294
}
8395

8496
@Test
85-
public void testPolicyCheckFailed_ClientError() throws AttestationException {
97+
public void testPolicyCheckSuccess_ClientError() throws AttestationException {
8698
var errorStr = "policy validation failed";
8799
when(alwaysFailPolicyValidator.validate(any(), any())).thenThrow(new AttestationClientException(errorStr, AttestationFailure.BAD_PAYLOAD));
88100
var provider = new AzureCCCoreAttestationService(alwaysFailTokenValidator, alwaysFailPolicyValidator);

src/test/java/com/uid2/shared/secure/azurecc/PolicyValidatorTest.java

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,40 @@
11
package com.uid2.shared.secure.azurecc;
22

3+
import com.uid2.shared.secure.AttestationClientException;
34
import com.uid2.shared.secure.AttestationException;
5+
import com.uid2.shared.secure.AttestationFailure;
46
import org.junit.jupiter.api.Test;
57

8+
import java.nio.ByteBuffer;
9+
import java.nio.charset.StandardCharsets;
10+
import java.util.Arrays;
11+
import java.util.Base64;
12+
613
import static org.junit.jupiter.api.Assertions.assertEquals;
714
import static org.junit.jupiter.api.Assertions.assertThrows;
815

916
class PolicyValidatorTest {
17+
private static byte[] encodeStringUnicodeAttestationEndpoint(String data) {
18+
// buffer.array() may include extra empty bytes at the end. This returns only the bytes that have data
19+
ByteBuffer buffer = StandardCharsets.UTF_8.encode(data);
20+
return Arrays.copyOf(buffer.array(), buffer.limit());
21+
}
1022
private static final String PUBLIC_KEY = "public_key";
1123
private static final String CCE_POLICY_DIGEST = "digest";
24+
private static final String ATTESTATION_URL = "https://example.com";
25+
private static final String ENCODED_ATTESTATION_URL = Base64.getEncoder().encodeToString(encodeStringUnicodeAttestationEndpoint(ATTESTATION_URL));
1226

1327
@Test
1428
public void testValidationSuccess() throws AttestationException {
15-
var validator = new PolicyValidator();
29+
var validator = new PolicyValidator(ATTESTATION_URL);
1630
var payload = generateBasicPayload();
1731
var enclaveId = validator.validate(payload, PUBLIC_KEY);
1832
assertEquals(CCE_POLICY_DIGEST, enclaveId);
1933
}
2034

2135
@Test
2236
public void testValidationFailure_VMInfo() throws AttestationException {
23-
var validator = new PolicyValidator();
37+
var validator = new PolicyValidator(ATTESTATION_URL);
2438
var newPayload = generateBasicPayload()
2539
.toBuilder()
2640
.attestationType("dummy")
@@ -30,7 +44,7 @@ public void testValidationFailure_VMInfo() throws AttestationException {
3044

3145
@Test
3246
public void testValidationFailure_UVMInfo() throws AttestationException {
33-
var validator = new PolicyValidator();
47+
var validator = new PolicyValidator(ATTESTATION_URL);
3448
var newPayload = generateBasicPayload()
3549
.toBuilder()
3650
.complianceStatus("dummy")
@@ -40,7 +54,7 @@ public void testValidationFailure_UVMInfo() throws AttestationException {
4054

4155
@Test
4256
public void testValidationFailure_VMDebug() throws AttestationException {
43-
var validator = new PolicyValidator();
57+
var validator = new PolicyValidator(ATTESTATION_URL);
4458
var newPayload = generateBasicPayload()
4559
.toBuilder()
4660
.vmDebuggable(true)
@@ -54,7 +68,7 @@ public void testValidationFailure_PublicKeyNotMatch() throws AttestationExceptio
5468
.toBuilder()
5569
.publicKey("dummy")
5670
.build();
57-
var validator = new PolicyValidator();
71+
var validator = new PolicyValidator(ATTESTATION_URL);
5872
var newPayload = generateBasicPayload()
5973
.toBuilder()
6074
.runtimeData(newRunTimeData)
@@ -68,7 +82,7 @@ public void testValidationFailure_LocationNotSupported() throws AttestationExcep
6882
.toBuilder()
6983
.location("West Europe")
7084
.build();
71-
var validator = new PolicyValidator();
85+
var validator = new PolicyValidator(ATTESTATION_URL);
7286
var newPayload = generateBasicPayload()
7387
.toBuilder()
7488
.runtimeData(newRunTimeData)
@@ -89,7 +103,26 @@ private MaaTokenPayload generateBasicPayload() {
89103
private RuntimeData generateBasicRuntimeData(){
90104
return RuntimeData.builder()
91105
.publicKey(PUBLIC_KEY)
106+
.attestationUrl(ENCODED_ATTESTATION_URL)
92107
.location("East US")
93108
.build();
94109
}
110+
111+
@Test
112+
public void testValidationSuccess_SameAttestationUrl() throws AttestationException {
113+
var validator = new PolicyValidator(ATTESTATION_URL);
114+
var payload = generateBasicPayload();
115+
var enclaveId = validator.validate(payload, PUBLIC_KEY);
116+
assertEquals(CCE_POLICY_DIGEST, enclaveId);
117+
}
118+
119+
@Test
120+
public void testValidationFailure_DifferentAttestationUrl() {
121+
var validator = new PolicyValidator("https://someother.uidapi.com");
122+
var payload = generateBasicPayload();
123+
Throwable t = assertThrows(AttestationException.class, ()-> validator.validate(payload, PUBLIC_KEY));
124+
assertEquals("The given attestation URL is unknown. Given URL: " + ATTESTATION_URL, t.getMessage());
125+
assertEquals(AttestationFailure.UNKNOWN_ATTESTATION_URL, ((AttestationClientException)t).getAttestationFailure());
126+
127+
}
95128
}

0 commit comments

Comments
 (0)