Skip to content

Commit a25e6b1

Browse files
committed
Update attestation failure handling
1 parent c26149d commit a25e6b1

8 files changed

+186
-94
lines changed

pom.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
<groupId>com.uid2</groupId>
77
<artifactId>uid2-shared</artifactId>
8-
<version>7.19.0</version>
8+
<version>7.19.1-SNAPSHOT</version>
99
<name>${project.groupId}:${project.artifactId}</name>
1010
<description>Library for all the shared uid2 operations</description>
1111
<url>https://github.com/IABTechLab/uid2docs</url>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package com.uid2.shared.attest;
2+
3+
public enum AttestationResponseCode {
4+
AttestationFailure,
5+
RetryableFailure,
6+
Success
7+
}

src/main/java/com/uid2/shared/attest/AttestationResponseHandler.java

+36-33
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import io.vertx.core.Vertx;
88
import io.vertx.core.json.Json;
99
import io.vertx.core.json.JsonObject;
10+
import lombok.Getter;
1011
import org.slf4j.Logger;
1112
import org.slf4j.LoggerFactory;
1213
import software.amazon.awssdk.utils.Pair;
@@ -34,7 +35,7 @@ public class AttestationResponseHandler {
3435
private final AtomicReference<String> attestationToken;
3536
private final AtomicReference<String> optOutJwt;
3637
private final AtomicReference<String> coreJwt;
37-
private final Handler<Pair<Integer, String>> responseWatcher;
38+
private final Handler<Pair<AttestationResponseCode, String>> responseWatcher;
3839
private final String attestationEndpoint;
3940
private final byte[] encodedAttestationEndpoint;
4041
private final IClock clock;
@@ -46,6 +47,7 @@ public class AttestationResponseHandler {
4647
private Instant attestationTokenExpiresAt = Instant.MAX;
4748
private final Lock lock;
4849
private final AttestationTokenDecryptor attestationTokenDecryptor;
50+
@Getter
4951
private final String appVersionHeader;
5052
private final int attestCheckMilliseconds;
5153
private final AtomicReference<String> optOutUrl;
@@ -56,17 +58,18 @@ public AttestationResponseHandler(Vertx vertx,
5658
String operatorType,
5759
ApplicationVersion appVersion,
5860
IAttestationProvider attestationProvider,
59-
Handler<Pair<Integer, String>> responseWatcher,
61+
Handler<Pair<AttestationResponseCode, String>> responseWatcher,
6062
Proxy proxy) {
6163
this(vertx, attestationEndpoint, clientApiToken, operatorType, appVersion, attestationProvider, responseWatcher, proxy, new InstantClock(), null, null, 60000);
6264
}
65+
6366
public AttestationResponseHandler(Vertx vertx,
6467
String attestationEndpoint,
6568
String clientApiToken,
6669
String operatorType,
6770
ApplicationVersion appVersion,
6871
IAttestationProvider attestationProvider,
69-
Handler<Pair<Integer, String>> responseWatcher,
72+
Handler<Pair<AttestationResponseCode, String>> responseWatcher,
7073
Proxy proxy,
7174
IClock clock,
7275
URLConnectionHttpClient httpClient,
@@ -131,16 +134,7 @@ private void attestationExpirationCheck(long timerId) {
131134
}
132135

133136
attest();
134-
} catch (AttestationResponseHandlerException e) {
135-
if (e.isAttestationFailure()) {
136-
notifyResponseWatcher(401, e.getMessage());
137-
LOGGER.info("Re-attest failed with attestation failure: ", e);
138-
} else {
139-
notifyResponseWatcher(e.getStatusCode(), e.getMessage());
140-
LOGGER.info("Re-attest failed with retryable failure: ", e);
141-
}
142-
} catch (IOException e){
143-
notifyResponseWatcher(500, e.getMessage());
137+
} catch (AttestationResponseHandlerException | IOException e) {
144138
LOGGER.info("Re-attest failed: ", e);
145139
} finally {
146140
this.isAttesting.set(false);
@@ -185,35 +179,32 @@ public void attest() throws IOException, AttestationResponseHandlerException {
185179

186180
int statusCode = response.statusCode();
187181
String responseBody = response.body();
188-
notifyResponseWatcher(statusCode, responseBody);
189182

190-
if (statusCode == 401 || statusCode == 403) {
191-
LOGGER.warn("attestation failed with UID2 Core returning statusCode={}", statusCode);
192-
throw new AttestationResponseHandlerException(statusCode, "Attestation Failure response from Core");
193-
}
183+
AttestationResponseCode responseCode = this.getAttestationResponseCodeFromHttpStatus(statusCode);
184+
185+
notifyResponseWatcher(responseCode, responseBody);
194186

195-
if (statusCode < 200 || statusCode >= 300) {
196-
LOGGER.warn("attestation failed with UID2 Core returning statusCode={}", statusCode);
197-
throw new AttestationResponseHandlerException(statusCode, "unexpected status code from uid core service");
187+
if (responseCode != AttestationResponseCode.Success) {
188+
throw new AttestationResponseHandlerException(responseCode, "Non-success response from Core on attest");
198189
}
199190

200191
JsonObject responseJson = (JsonObject) Json.decodeValue(responseBody);
201192
if (isFailed(responseJson)) {
202-
throw new AttestationResponseHandlerException(statusCode, "response did not return a successful status");
193+
throw new AttestationResponseHandlerException(AttestationResponseCode.RetryableFailure, "response did not return a successful status");
203194
}
204195

205196
JsonObject innerBody = responseJson.getJsonObject("body");
206197
if (innerBody == null) {
207-
throw new AttestationResponseHandlerException(statusCode, "response did not contain a body object");
198+
throw new AttestationResponseHandlerException(AttestationResponseCode.RetryableFailure, "response did not contain a body object");
208199
}
209200

210201
String atoken = getAttestationToken(innerBody);
211202
if (atoken == null) {
212-
throw new AttestationResponseHandlerException(statusCode, "response json does not contain body.attestation_token");
203+
throw new AttestationResponseHandlerException(AttestationResponseCode.RetryableFailure, "response json does not contain body.attestation_token");
213204
}
214205
String expiresAt = getAttestationTokenExpiresAt(innerBody);
215206
if (expiresAt == null) {
216-
throw new AttestationResponseHandlerException(statusCode, "response json does not contain body.expiresAt");
207+
throw new AttestationResponseHandlerException(AttestationResponseCode.RetryableFailure, "response json does not contain body.expiresAt");
217208
}
218209

219210
atoken = new String(attestationTokenDecryptor.decrypt(Base64.getDecoder().decode(atoken), keyPair.getPrivate()), StandardCharsets.UTF_8);
@@ -225,8 +216,8 @@ public void attest() throws IOException, AttestationResponseHandlerException {
225216
setOptoutURLFromResponse(innerBody);
226217

227218
scheduleAttestationExpirationCheck();
228-
} catch (IOException ioe) {
229-
throw ioe;
219+
} catch (AttestationResponseHandlerException | IOException e) {
220+
throw e;
230221
} catch (Exception e) {
231222
throw new AttestationResponseHandlerException(e);
232223
}
@@ -252,10 +243,6 @@ public String getOptOutUrl() {
252243
return this.optOutUrl.get();
253244
}
254245

255-
public String getAppVersionHeader() {
256-
return this.appVersionHeader;
257-
}
258-
259246
private void setAttestationTokenExpiresAt(String expiresAt) {
260247
this.attestationTokenExpiresAt = Instant.parse(expiresAt);
261248
}
@@ -309,11 +296,15 @@ private static KeyPair generateKeyPair() throws NoSuchAlgorithmException {
309296
return gen.generateKeyPair();
310297
}
311298

312-
private void notifyResponseWatcher(int statusCode, String responseBody) {
299+
private void notifyResponseWatcher(AttestationResponseCode responseCode, String responseBody) {
300+
if (responseCode != AttestationResponseCode.Success) {
301+
LOGGER.warn("Received a non-success response code on Attestation: ResponseCode: {}, Message: {}", responseCode, responseBody);
302+
}
303+
313304
this.lock.lock();
314305
try {
315306
if (this.responseWatcher != null)
316-
this.responseWatcher.handle(Pair.of(statusCode, responseBody));
307+
this.responseWatcher.handle(Pair.of(responseCode, responseBody));
317308
} finally {
318309
lock.unlock();
319310
}
@@ -328,4 +319,16 @@ private byte[] encodeStringUnicodeAttestationEndpoint(String data) {
328319
ByteBuffer buffer = StandardCharsets.UTF_8.encode(data);
329320
return Arrays.copyOf(buffer.array(), buffer.limit());
330321
}
322+
323+
private AttestationResponseCode getAttestationResponseCodeFromHttpStatus(int httpStatus) {
324+
if (httpStatus == 401 || httpStatus == 403) {
325+
return AttestationResponseCode.AttestationFailure;
326+
}
327+
328+
if (httpStatus == 200) {
329+
return AttestationResponseCode.Success;
330+
}
331+
332+
return AttestationResponseCode.RetryableFailure;
333+
}
331334
}

src/main/java/com/uid2/shared/attest/AttestationResponseHandlerException.java

+5-5
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
@Getter
66
public class AttestationResponseHandlerException extends Exception {
7-
private int statusCode = 0;
7+
private AttestationResponseCode responseCode;
88

99
public AttestationResponseHandlerException(Throwable t) {
1010
super(t);
@@ -14,13 +14,13 @@ public AttestationResponseHandlerException(String message) {
1414
super(message);
1515
}
1616

17-
public AttestationResponseHandlerException(int statusCode, String message) {
18-
super("http status: " + String.valueOf(statusCode) + ", " + message);
19-
this.statusCode = statusCode;
17+
public AttestationResponseHandlerException(AttestationResponseCode responseCode, String message) {
18+
super("AttestationResponseCode: " + String.valueOf(responseCode) + ", " + message);
19+
this.responseCode = responseCode;
2020
}
2121

2222
public boolean isAttestationFailure() {
23-
return statusCode == 401 || statusCode == 403;
23+
return responseCode == AttestationResponseCode.AttestationFailure;
2424
}
2525

2626
}

src/main/java/com/uid2/shared/attest/UidCoreClient.java

+1-10
Original file line numberDiff line numberDiff line change
@@ -80,9 +80,8 @@ private InputStream internalDownload(String path) throws CloudStorageException {
8080
}
8181
return inputStream;
8282
} catch (Exception e) {
83-
throw new CloudStorageException("download " + path + " error: " + e.getMessage(), e);
83+
throw new CloudStorageException("download error: " + e.getMessage(), e);
8484
}
85-
8685
}
8786

8887
private InputStream readContentFromLocalFileSystem(String path, Proxy proxy) throws IOException {
@@ -99,14 +98,6 @@ private InputStream getWithAttest(String path) throws IOException, AttestationRe
9998
HttpResponse<String> httpResponse;
10099
httpResponse = sendHttpRequest(path, attestationToken);
101100

102-
// This should never happen, but keeping this part of the code just to be extra safe.
103-
if (httpResponse.statusCode() == 401) {
104-
LOGGER.info("Initial response from UID2 Core returned 401, performing attestation");
105-
attestationResponseHandler.attest();
106-
attestationToken = attestationResponseHandler.getAttestationToken();
107-
httpResponse = sendHttpRequest(path, attestationToken);
108-
}
109-
110101
return Utils.convertHttpResponseToInputStream(httpResponse);
111102
}
112103

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

+12
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ public enum AttestationFailure {
77
BAD_CERTIFICATE,
88
FORBIDDEN_ENCLAVE,
99
UNKNOWN_ATTESTATION_URL,
10+
INVALID_PROTOCOL,
11+
INTERNAL_ERROR,
12+
INVALID_TYPE,
13+
RESPONSE_ENCRYPTION_ERROR,
1014
UNKNOWN;
1115

1216
public String explain() {
@@ -23,6 +27,14 @@ public String explain() {
2327
return "The enclave identifier is unknown";
2428
case UNKNOWN_ATTESTATION_URL:
2529
return "The given attestation URL is unknown";
30+
case INVALID_PROTOCOL:
31+
return "The given protocol is not valid";
32+
case INTERNAL_ERROR:
33+
return "There was an internal processing error";
34+
case INVALID_TYPE:
35+
return "Invalid Operator Type";
36+
case RESPONSE_ENCRYPTION_ERROR:
37+
return "Error encrypting the response";
2638
default:
2739
return "Unknown reason";
2840
}

0 commit comments

Comments
 (0)