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

Commit

Permalink
Add random key padding (#609)
Browse files Browse the repository at this point in the history
  • Loading branch information
pithumke authored Jun 22, 2020
1 parent e3d9044 commit 3d58c8f
Show file tree
Hide file tree
Showing 5 changed files with 85 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,25 @@
package app.coronawarn.server.services.submission.config;

import java.io.File;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import org.springframework.validation.annotation.Validated;

@Component
@ConfigurationProperties(prefix = "services.submission")
@Validated
public class SubmissionServiceConfig {

// Exponential moving average of the last N real request durations (in ms), where
// N = fakeDelayMovingAverageSamples.
private Long initialFakeDelayMilliseconds;
private Long fakeDelayMovingAverageSamples;
private Integer retentionDays;
@Min(1)
@Max(100)
private Integer randomKeyPaddingMultiplier;
private Integer connectionPoolSize;
private Payload payload;
private Verification verification;
Expand Down Expand Up @@ -63,6 +70,14 @@ public void setRetentionDays(Integer retentionDays) {
this.retentionDays = retentionDays;
}

public Integer getRandomKeyPaddingMultiplier() {
return randomKeyPaddingMultiplier;
}

public void setRandomKeyPaddingMultiplier(Integer randomKeyPaddingMultiplier) {
this.randomKeyPaddingMultiplier = randomKeyPaddingMultiplier;
}

public Integer getConnectionPoolSize() {
return connectionPoolSize;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,10 @@
import app.coronawarn.server.services.submission.validation.ValidSubmissionPayload;
import app.coronawarn.server.services.submission.verification.TanVerifier;
import io.micrometer.core.annotation.Timed;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.IntStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
Expand Down Expand Up @@ -59,6 +61,7 @@ public class SubmissionController {
private final DiagnosisKeyService diagnosisKeyService;
private final TanVerifier tanVerifier;
private final Integer retentionDays;
private final Integer randomKeyPaddingMultiplier;
private final FakeDelayManager fakeDelayManager;

SubmissionController(
Expand All @@ -69,6 +72,7 @@ public class SubmissionController {
this.submissionMonitor = submissionMonitor;
this.fakeDelayManager = fakeDelayManager;
retentionDays = submissionServiceConfig.getRetentionDays();
randomKeyPaddingMultiplier = submissionServiceConfig.getRandomKeyPaddingMultiplier();
}

/**
Expand Down Expand Up @@ -130,6 +134,26 @@ public void persistDiagnosisKeysPayload(SubmissionPayload protoBufDiagnosisKeys)
}
}

diagnosisKeyService.saveDiagnosisKeys(diagnosisKeys);
diagnosisKeyService.saveDiagnosisKeys(padDiagnosisKeys(diagnosisKeys));
}

private List<DiagnosisKey> padDiagnosisKeys(List<DiagnosisKey> diagnosisKeys) {
List<DiagnosisKey> paddedDiagnosisKeys = new ArrayList<>();
diagnosisKeys.forEach(diagnosisKey -> {
paddedDiagnosisKeys.add(diagnosisKey);
IntStream.range(1, randomKeyPaddingMultiplier)
.mapToObj(index -> {
byte[] randomKeyData = new byte[16];
new SecureRandom().nextBytes(randomKeyData);
return DiagnosisKey.builder()
.withKeyData(randomKeyData)
.withRollingStartIntervalNumber(diagnosisKey.getRollingStartIntervalNumber())
.withTransmissionRiskLevel(diagnosisKey.getTransmissionRiskLevel())
.withRollingPeriod(diagnosisKey.getRollingPeriod())
.build();
})
.forEach(paddedDiagnosisKeys::add);
});
return paddedDiagnosisKeys;
}
}
6 changes: 6 additions & 0 deletions services/submission/src/main/resources/application.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ services:
fake-delay-moving-average-samples: 10
# The retention threshold for acceptable diagnosis keys during submission.
retention-days: 14
# The number of keys to save to the DB for every real submitted key.
# Example: If the 'random-key-padding-multiplier' is set to 10, and 5 keys are being submitted,
# then the 5 real submitted keys will be saved to the DB, plus an additional 45 keys with
# random 'key_data'. All properties, besides the 'key_data', of the additional keys will be
# identical to the real key.
random-key-padding-multiplier: ${RANDOM_KEY_PADDING_MULTIPLIER:1}
# The ApacheHttpClient's connection pool size.
connection-pool-size: 200
payload:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,28 +127,28 @@ void singleKeyWithOutdatedRollingStartIntervalNumberDoesNotGetSaved() {

@Test
void keysWithOutdatedRollingStartIntervalNumberDoNotGetSaved() {
Collection<TemporaryExposureKey> keys = buildPayloadWithMultipleKeys();
Collection<TemporaryExposureKey> submittedKeys = buildPayloadWithMultipleKeys();
TemporaryExposureKey outdatedKey = createOutdatedKey();
keys.add(outdatedKey);
submittedKeys.add(outdatedKey);
ArgumentCaptor<Collection<DiagnosisKey>> argument = ArgumentCaptor.forClass(Collection.class);

executor.executePost(keys, buildOkHeaders());
executor.executePost(submittedKeys, buildOkHeaders());

verify(diagnosisKeyService, atLeastOnce()).saveDiagnosisKeys(argument.capture());
keys.remove(outdatedKey);
assertElementsCorrespondToEachOther(keys, argument.getValue());
submittedKeys.remove(outdatedKey);
assertElementsCorrespondToEachOther(submittedKeys, argument.getValue());
}

@Test
void checkSaveOperationCallAndFakeDelayUpdateForValidParameters() {
Collection<TemporaryExposureKey> keys = buildPayloadWithMultipleKeys();
Collection<TemporaryExposureKey> submittedKeys = buildPayloadWithMultipleKeys();
ArgumentCaptor<Collection<DiagnosisKey>> argument = ArgumentCaptor.forClass(Collection.class);

executor.executePost(keys, buildOkHeaders());
executor.executePost(submittedKeys, buildOkHeaders());

verify(diagnosisKeyService, atLeastOnce()).saveDiagnosisKeys(argument.capture());
verify(fakeDelayManager, times(1)).updateFakeRequestDelay(anyLong());
assertElementsCorrespondToEachOther(keys, argument.getValue());
assertElementsCorrespondToEachOther(submittedKeys, argument.getValue());
}

@ParameterizedTest
Expand Down Expand Up @@ -186,7 +186,7 @@ private static Stream<Arguments> createDeniedHttpMethods() {
return Arrays.stream(HttpMethod.values())
.filter(method -> method != HttpMethod.POST)
.filter(method -> method != HttpMethod.PATCH) /* not supported by Rest Template */
.map(elem -> Arguments.of(elem));
.map(Arguments::of);
}

@Test
Expand Down Expand Up @@ -252,18 +252,36 @@ private static Collection<TemporaryExposureKey> buildPayloadWithInvalidKey() {
.collect(Collectors.toCollection(ArrayList::new));
}

private void assertElementsCorrespondToEachOther
(Collection<TemporaryExposureKey> submittedKeys, Collection<DiagnosisKey> keyEntities) {
Set<DiagnosisKey> expKeys = submittedKeys.stream()
.map(aSubmittedKey -> DiagnosisKey.builder().fromProtoBuf(aSubmittedKey).build())
private void assertElementsCorrespondToEachOther(Collection<TemporaryExposureKey> submittedTemporaryExposureKeys,
Collection<DiagnosisKey> savedDiagnosisKeys) {

Set<DiagnosisKey> submittedDiagnosisKeys = submittedTemporaryExposureKeys.stream()
.map(submittedDiagnosisKey -> DiagnosisKey.builder().fromProtoBuf(submittedDiagnosisKey).build())
.collect(Collectors.toSet());

assertThat(keyEntities)
.withFailMessage("Number of submitted keys and generated key entities don't match.")
.hasSameSizeAs(expKeys);
keyEntities.forEach(anActKey -> assertThat(expKeys)
.withFailMessage("Key entity does not correspond to a submitted key.")
.contains(anActKey)
);
assertThat(savedDiagnosisKeys).hasSize(submittedDiagnosisKeys.size() * 10);
assertThat(savedDiagnosisKeys).containsAll(submittedDiagnosisKeys);

submittedDiagnosisKeys.forEach(submittedDiagnosisKey -> {
List<DiagnosisKey> savedKeysForSingleSubmittedKey = savedDiagnosisKeys.stream()
.filter(savedDiagnosisKey -> savedDiagnosisKey.getRollingPeriod() ==
submittedDiagnosisKey.getRollingPeriod())
.filter(savedDiagnosisKey -> savedDiagnosisKey.getTransmissionRiskLevel() ==
submittedDiagnosisKey.getTransmissionRiskLevel())
.filter(savedDiagnosisKey -> savedDiagnosisKey.getRollingStartIntervalNumber() ==
submittedDiagnosisKey.getRollingStartIntervalNumber())
.collect(Collectors.toList());

assertThat(savedKeysForSingleSubmittedKey).hasSize(10);
assertThat(savedKeysForSingleSubmittedKey.stream().filter(savedKey ->
Arrays.equals(savedKey.getKeyData(), submittedDiagnosisKey.getKeyData()))).hasSize(1);
assertThat(savedKeysForSingleSubmittedKey).allMatch(
savedKey -> savedKey.getRollingPeriod() == submittedDiagnosisKey.getRollingPeriod());
assertThat(savedKeysForSingleSubmittedKey).allMatch(
savedKey -> savedKey.getRollingStartIntervalNumber() == submittedDiagnosisKey
.getRollingStartIntervalNumber());
assertThat(savedKeysForSingleSubmittedKey).allMatch(
savedKey -> savedKey.getTransmissionRiskLevel() == submittedDiagnosisKey.getTransmissionRiskLevel());
});
}
}
1 change: 1 addition & 0 deletions services/submission/src/test/resources/application.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ services:
initial-fake-delay-milliseconds: 1
fake-delay-moving-average-samples: 1
retention-days: 14
random-key-padding-multiplier: 10
connection-pool-size: 200
payload:
max-number-of-keys: 14
Expand Down

0 comments on commit 3d58c8f

Please sign in to comment.