Skip to content
This repository has been archived by the owner on May 16, 2023. It is now read-only.

Commit

Permalink
Merge pull request #30 from corona-warn-app/feat/signing-api-header
Browse files Browse the repository at this point in the history
Add Location-Id and Transaction-Id Header for UBirch API
  • Loading branch information
ascheibal committed Jun 16, 2021
2 parents 4c19e6e + 0fe933d commit 7298166
Show file tree
Hide file tree
Showing 6 changed files with 56 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;

/**
* This class represents the Verification Server Feign client.
Expand All @@ -44,5 +45,8 @@ public interface SigningApiClient {
consumes = MediaType.TEXT_PLAIN_VALUE,
produces = MediaType.APPLICATION_CBOR_VALUE
)
byte[] sign(@RequestBody String hash);
byte[] sign(
@RequestBody String hash,
@RequestHeader("X-Location-Id") String labIdHash,
@RequestHeader("X-Transaction-Id") String dcciHash);
}
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ public Client signingApiClient() {

List<BasicHeader> headers = new ArrayList<>();

if (config.getSigningApiServer().getApiKey() != null) {
if (!config.getSigningApiServer().getApiKey().isEmpty()) {
headers.add(
new BasicHeader(HttpHeaders.AUTHORIZATION, "Bearer " + config.getSigningApiServer().getApiKey()));
}
Expand Down
13 changes: 9 additions & 4 deletions src/main/java/app/coronawarn/dcc/service/DccService.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ public class DccService {

private final SigningApiClient signingApiClient;

private final HashingService hashingService;

/**
* Creates signed data for a DCCRegistration.
* This Endpoints queries the SigningAPI and signs the hash assigned to this Registration.
Expand All @@ -58,7 +60,10 @@ public DccRegistration sign(DccRegistration registration) throws DccGenerateExce
byte[] hashBytes = Hex.decode(registration.getDccHash());
String hashBase64 = Base64.getEncoder().encodeToString(hashBytes);

coseBytes = callSigningApiWithRetry(hashBase64);
coseBytes = callSigningApiWithRetry(
hashBase64,
hashingService.hash(registration.getLabId()),
hashingService.hash(registration.getDcci()));
} catch (FeignException e) {
log.error("Failed to sign DCC. Http Status Code: {}, Message: {}", e.status(), e.getMessage());

Expand All @@ -81,12 +86,12 @@ public DccRegistration sign(DccRegistration registration) throws DccGenerateExce
return registration;
}

private byte[] callSigningApiWithRetry(String hashBase64) {
private byte[] callSigningApiWithRetry(String hashBase64, String hashedLabId, String hashedDcci) {
try {
return signingApiClient.sign(hashBase64);
return signingApiClient.sign(hashBase64, hashedLabId, hashedDcci);
} catch (FeignException e) {
log.info("First try of calling Signing API failed. Status Code: {}, Message: {}", e.status(), e.getMessage());
return signingApiClient.sign(hashBase64);
return signingApiClient.sign(hashBase64, hashedLabId, hashedDcci);
}
}

Expand Down
4 changes: 2 additions & 2 deletions src/main/resources/application-cloud.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,9 @@ cwa:
key-store-password: ${CWA_DCC_SIGNINGAPISERVER_KEYSTOREPASSWORD}
trust-store-password: ${CWA_DCC_SIGNINGAPISERVER_TRUSTSTOREPASSWORD}
trust-store-path: ${CWA_DCC_SIGNINGAPISERVER_TRUSTSTOREPATH}
api-key: ${CWA_DCC_SIGNINGAPISERVER_APIKEY}
api-key: ${CWA_DCC_SIGNINGAPISERVER_APIKEY:}
verify-hostnames: true
connection-close-workaround: ${CWA_DCC_SIGNINGAPISERVER_CONNECTIONCLOSEWORKAROUND}
connection-close-workaround: ${CWA_DCC_SIGNINGAPISERVER_CONNECTIONCLOSEWORKAROUND:false}
request:
sizelimit: 10000
entities:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import static app.coronawarn.dcc.utils.TestValues.registrationTokenValue;
import static app.coronawarn.dcc.utils.TestValues.testId;
import static org.hamcrest.Matchers.equalTo;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.never;
Expand All @@ -52,6 +53,7 @@
import app.coronawarn.dcc.repository.DccRegistrationRepository;
import app.coronawarn.dcc.repository.LabIdClaimRepository;
import app.coronawarn.dcc.service.DccRegistrationService;
import app.coronawarn.dcc.service.HashingService;
import app.coronawarn.dcc.service.LabIdClaimService;
import app.coronawarn.dcc.utils.TestUtils;
import com.fasterxml.jackson.databind.ObjectMapper;
Expand Down Expand Up @@ -89,6 +91,9 @@ public class InternalDccControllerTest {
@Autowired
DccRegistrationService dccRegistrationService;

@Autowired
HashingService hashingService;

@MockBean
VerificationServerClient verificationServerClientMock;

Expand All @@ -103,7 +108,7 @@ void setup() {
when(verificationServerClientMock.result(eq(registrationToken)))
.thenReturn(new InternalTestResult(6, labId, testId, 0));

when(signingApiClientMock.sign(eq(dccHashBase64)))
when(signingApiClientMock.sign(eq(dccHashBase64), eq(hashingService.hash(labId)), anyString()))
.thenReturn(partialDcc);
}

Expand All @@ -123,7 +128,7 @@ void testUploadDcc() throws Exception {
.andExpect(status().isOk())
.andExpect(jsonPath("$.partialDcc").value(equalTo(partialDccBase64)));

verify(signingApiClientMock).sign(eq(dccHashBase64));
verify(signingApiClientMock).sign(eq(dccHashBase64), eq(hashingService.hash(labId)), anyString());

Optional<DccRegistration> dccRegistration = dccRegistrationRepository.findByRegistrationToken(registrationTokenValue);

Expand All @@ -147,7 +152,7 @@ void testUploadDccFailedUnknownTestId() throws Exception {
)
.andExpect(status().isNotFound());

verify(signingApiClientMock, never()).sign(eq(dccHashBase64));
verify(signingApiClientMock, never()).sign(eq(dccHashBase64), eq(hashingService.hash(labId)), anyString());
}

@Test
Expand All @@ -166,7 +171,7 @@ void testUploadDccFailedLabIdNotAssignedToPartner() throws Exception {
)
.andExpect(status().isForbidden());

verify(signingApiClientMock, never()).sign(eq(dccHashBase64));
verify(signingApiClientMock, never()).sign(eq(dccHashBase64), eq(hashingService.hash(labId)), anyString());
}

@Test
Expand All @@ -186,7 +191,7 @@ void testUploadDccFailedAlreadyExists() throws Exception {
)
.andExpect(status().isConflict());

verify(signingApiClientMock, never()).sign(eq(dccHashBase64));
verify(signingApiClientMock, never()).sign(eq(dccHashBase64), eq(hashingService.hash(labId)), anyString());
}

@Test
Expand All @@ -202,7 +207,7 @@ void testUploadDccFailedInvalidDCC() throws Exception {
)
.andExpect(status().isBadRequest());

verify(signingApiClientMock, never()).sign(eq(dccHashBase64));
verify(signingApiClientMock, never()).sign(eq(dccHashBase64), eq(hashingService.hash(labId)), anyString());
}

@Test
Expand All @@ -218,7 +223,7 @@ void testUploadDccFailedInvalidDek() throws Exception {
)
.andExpect(status().isBadRequest());

verify(signingApiClientMock, never()).sign(eq(dccHashBase64));
verify(signingApiClientMock, never()).sign(eq(dccHashBase64), eq(hashingService.hash(labId)), anyString());
}

@Test
Expand All @@ -228,7 +233,7 @@ void testUploadDccFailedBadResponseFromSignignAPI4xx() throws Exception {
DccUploadRequest dccUploadRequest = new DccUploadRequest(dccHash, encryptedDccBase64, encryptedDekBase64);

doThrow(new FeignException.BadRequest("", dummyRequest, null))
.when(signingApiClientMock).sign(eq(dccHashBase64));
.when(signingApiClientMock).sign(eq(dccHashBase64), eq(hashingService.hash(labId)), anyString());

mockMvc.perform(post("/version/v1/test/" + testId + "/dcc")
.contentType(MediaType.APPLICATION_JSON_VALUE)
Expand All @@ -254,7 +259,7 @@ void testUploadDccFailedBadResponseFromSignignAPI5xx() throws Exception {
DccUploadRequest dccUploadRequest = new DccUploadRequest(dccHash, encryptedDccBase64, encryptedDekBase64);

doThrow(new FeignException.InternalServerError("", dummyRequest, null))
.when(signingApiClientMock).sign(eq(dccHashBase64));
.when(signingApiClientMock).sign(eq(dccHashBase64), eq(hashingService.hash(labId)), anyString());

mockMvc.perform(post("/version/v1/test/" + testId + "/dcc")
.contentType(MediaType.APPLICATION_JSON_VALUE)
Expand Down
30 changes: 25 additions & 5 deletions src/test/java/app/coronawarn/dcc/service/DccServiceTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import static app.coronawarn.dcc.utils.TestValues.registrationToken;
import static app.coronawarn.dcc.utils.TestValues.registrationTokenValue;
import static app.coronawarn.dcc.utils.TestValues.testId;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.when;
Expand Down Expand Up @@ -65,6 +66,9 @@ public class DccServiceTest {
@Autowired
DccRegistrationRepository dccRegistrationRepository;

@Autowired
HashingService hashingService;

@MockBean
VerificationServerClient verificationServerClientMock;

Expand All @@ -80,7 +84,11 @@ void setup() {
void testSigning() throws NoSuchAlgorithmException, DccRegistrationService.DccRegistrationException {

when(verificationServerClientMock.result(eq(registrationToken))).thenReturn(new InternalTestResult(6, labId, testId, 0));
when(signingApiClient.sign(eq(Base64.getEncoder().encodeToString(Hex.decode(dccHash))))).thenReturn(partialDcc);
when(signingApiClient.sign(
eq(Base64.getEncoder().encodeToString(Hex.decode(dccHash))),
eq(hashingService.hash(labId)),
anyString()
)).thenReturn(partialDcc);

PublicKey publicKey = TestUtils.generateKeyPair().getPublic();
DccRegistration registration = dccRegistrationService.createDccRegistration(registrationTokenValue, publicKey);
Expand All @@ -105,7 +113,10 @@ void testSigningFailedBySigningApi4xx() throws NoSuchAlgorithmException, DccRegi
when(verificationServerClientMock.result(eq(registrationToken))).thenReturn(new InternalTestResult(6, labId, testId, 0));

doThrow(new FeignException.BadRequest("", dummyRequest, null))
.when(signingApiClient).sign(eq(Base64.getEncoder().encodeToString(Hex.decode(dccHash))));
.when(signingApiClient).sign(
eq(Base64.getEncoder().encodeToString(Hex.decode(dccHash))),
eq(hashingService.hash(labId)),
anyString());

PublicKey publicKey = TestUtils.generateKeyPair().getPublic();
DccRegistration registration = dccRegistrationService.createDccRegistration(registrationTokenValue, publicKey);
Expand All @@ -131,7 +142,10 @@ void testSigningFailedBySigningApi5xx() throws NoSuchAlgorithmException, DccRegi
when(verificationServerClientMock.result(eq(registrationToken))).thenReturn(new InternalTestResult(6, labId, testId, 0));

doThrow(new FeignException.InternalServerError("", dummyRequest, null))
.when(signingApiClient).sign(eq(Base64.getEncoder().encodeToString(Hex.decode(dccHash))));
.when(signingApiClient).sign(
eq(Base64.getEncoder().encodeToString(Hex.decode(dccHash))),
eq(hashingService.hash((labId))),
anyString());

PublicKey publicKey = TestUtils.generateKeyPair().getPublic();
DccRegistration registration = dccRegistrationService.createDccRegistration(registrationTokenValue, publicKey);
Expand All @@ -158,7 +172,10 @@ void testSigningRetry() throws NoSuchAlgorithmException, DccRegistrationService.

doThrow(new FeignException.InternalServerError("", dummyRequest, null))
.doReturn(partialDcc)
.when(signingApiClient).sign(eq(Base64.getEncoder().encodeToString(Hex.decode(dccHash))));
.when(signingApiClient).sign(
eq(Base64.getEncoder().encodeToString(Hex.decode(dccHash))),
eq(hashingService.hash(labId)),
anyString());

PublicKey publicKey = TestUtils.generateKeyPair().getPublic();
DccRegistration registration = dccRegistrationService.createDccRegistration(registrationTokenValue, publicKey);
Expand All @@ -185,7 +202,10 @@ void testSigningFailedOnFirstRequestButSuccessOnSecond() throws NoSuchAlgorithmE
doThrow(new FeignException.InternalServerError("", dummyRequest, null))
.doThrow(new FeignException.InternalServerError("", dummyRequest, null))
.doReturn(partialDcc)
.when(signingApiClient).sign(eq(Base64.getEncoder().encodeToString(Hex.decode(dccHash))));
.when(signingApiClient).sign(
eq(Base64.getEncoder().encodeToString(Hex.decode(dccHash))),
eq(hashingService.hash(labId)),
anyString());

PublicKey publicKey = TestUtils.generateKeyPair().getPublic();
DccRegistration registration = dccRegistrationService.createDccRegistration(registrationTokenValue, publicKey);
Expand Down

0 comments on commit 7298166

Please sign in to comment.