Skip to content

Commit 8c78bf9

Browse files
authored
Merge pull request #44 from statisticsnorway/cache-keys
Cache KMS keys
2 parents d4c4dda + 30e0a02 commit 8c78bf9

File tree

5 files changed

+48
-16
lines changed

5 files changed

+48
-16
lines changed

Makefile

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,12 @@ release-dryrun: ## Simulate a release in order to detect any issues
1111

1212
.PHONY: release
1313
release: ## Release a new version. Update POMs and tag the new version in git
14-
git push origin master:release
14+
@set -e ; \
15+
git checkout master && \
16+
git pull && \
17+
git checkout release && \
18+
git merge master && \
19+
git push
1520

1621
.PHONY: help
1722
help:

pom.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
<tink.version>1.8.0</tink.version>
2727
<univocity-parsers.version>2.9.1</univocity-parsers.version>
2828
<zip4j.version>2.11.5</zip4j.version>
29+
<caffeine.version>3.2.2</caffeine.version>
2930

3031
<!-- Plugin/extension versions -->
3132
<artifactregistry-maven-wagon.version>2.1.4</artifactregistry-maven-wagon.version>
@@ -152,6 +153,11 @@
152153
<artifactId>univocity-parsers</artifactId>
153154
<version>${univocity-parsers.version}</version>
154155
</dependency>
156+
<dependency>
157+
<groupId>com.github.ben-manes.caffeine</groupId>
158+
<artifactId>caffeine</artifactId>
159+
<version>${caffeine.version}</version>
160+
</dependency>
155161
<dependency>
156162
<groupId>ch.qos.logback</groupId>
157163
<artifactId>logback-classic</artifactId>

src/main/java/no/ssb/dlp/pseudo/core/PseudoSecret.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
@Builder
1717
public class PseudoSecret implements Serializable {
1818
private String name;
19+
1920
private String id;
2021
private String version;
2122
private byte[] content;
@@ -48,5 +49,4 @@ private static byte[] base64DecodedContentOf(byte[] base64EncodedContent) {
4849
throw new PseudoException("Invalid secret. Content must be base64 encoded.");
4950
}
5051
}
51-
5252
}

src/main/java/no/ssb/dlp/pseudo/core/field/FieldPseudonymizer.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
package no.ssb.dlp.pseudo.core.field;
22

3+
import com.github.benmanes.caffeine.cache.Cache;
4+
import com.github.benmanes.caffeine.cache.LoadingCache;
5+
import com.google.crypto.tink.Aead;
36
import no.ssb.dapla.dlp.pseudo.func.PseudoFuncInput;
47
import no.ssb.dapla.dlp.pseudo.func.PseudoFuncOutput;
58
import no.ssb.dapla.dlp.pseudo.func.TransformDirection;
@@ -71,7 +74,7 @@ private PseudoFuncOutput process(PseudoOperation operation, FieldDescriptor fiel
7174
public static class Builder {
7275
private Collection<PseudoSecret> secrets;
7376
private Collection<PseudoFuncRule> rules;
74-
77+
private LoadingCache<String, Aead> aeadCache;
7578
private Collection<PseudoKeyset> keysets;
7679

7780
public Builder secrets(Collection<PseudoSecret> secrets) {
@@ -84,6 +87,11 @@ public Builder rules(Collection<PseudoFuncRule> rules) {
8487
return this;
8588
}
8689

90+
public Builder aeadCache(LoadingCache<String, Aead> aeadCache) {
91+
this.aeadCache = aeadCache;
92+
return this;
93+
}
94+
8795
public Builder keysets(Collection<PseudoKeyset> keysets) {
8896
this.keysets = keysets;
8997
return this;
@@ -92,7 +100,8 @@ public Builder keysets(Collection<PseudoKeyset> keysets) {
92100
public FieldPseudonymizer build() {
93101
Objects.requireNonNull(secrets, "PseudoSecrets can't be null");
94102
Objects.requireNonNull(rules, "PseudoFuncRule collection can't be null");
95-
return new FieldPseudonymizer(new PseudoFuncs(rules, secrets, keysets));
103+
Objects.requireNonNull(aeadCache, "AeadCache can't be null");
104+
return new FieldPseudonymizer(new PseudoFuncs(rules, secrets, keysets, aeadCache));
96105
}
97106
}
98107
}

src/main/java/no/ssb/dlp/pseudo/core/func/PseudoFuncs.java

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
package no.ssb.dlp.pseudo.core.func;
22

3-
import com.google.crypto.tink.DeterministicAead;
4-
import com.google.crypto.tink.JsonKeysetReader;
5-
import com.google.crypto.tink.KeysetHandle;
6-
import com.google.crypto.tink.KmsClients;
3+
import com.github.benmanes.caffeine.cache.Cache;
4+
import com.github.benmanes.caffeine.cache.LoadingCache;
5+
import com.google.crypto.tink.*;
76
import no.ssb.crypto.tink.fpe.Fpe;
87
import no.ssb.dapla.dlp.pseudo.func.PseudoFunc;
98
import no.ssb.dapla.dlp.pseudo.func.PseudoFuncConfig;
9+
import no.ssb.dapla.dlp.pseudo.func.PseudoFuncException;
1010
import no.ssb.dapla.dlp.pseudo.func.PseudoFuncFactory;
1111
import no.ssb.dapla.dlp.pseudo.func.composite.MapAndEncryptFunc;
1212
import no.ssb.dapla.dlp.pseudo.func.composite.MapAndEncryptFuncConfig;
@@ -24,24 +24,28 @@
2424
import no.ssb.dlp.pseudo.core.tink.model.EncryptedKeysetWrapper;
2525
import no.ssb.dlp.pseudo.core.util.Json;
2626

27+
import java.security.GeneralSecurityException;
28+
import java.time.Duration;
2729
import java.util.*;
2830
import java.util.function.Function;
2931
import java.util.stream.Collectors;
3032

3133
public class PseudoFuncs {
3234

3335
private final Map<PseudoFuncRule, PseudoFunc> ruleToFuncMap = new LinkedHashMap<>();
36+
private final LoadingCache<String, Aead> aeadCache;
3437

3538

3639
//TODO: Validate that all required secrets are available
3740
public PseudoFuncs(Collection<PseudoFuncRule> rules, Collection<PseudoSecret> pseudoSecrets,
38-
Collection<PseudoKeyset> keysets) {
41+
Collection<PseudoKeyset> keysets, LoadingCache<String, Aead> aeadCache) {
42+
this.aeadCache = aeadCache;
3943
Map<PseudoFuncRule, PseudoFuncConfig> ruleToPseudoFuncConfigs = initPseudoFuncConfigs(rules, pseudoSecrets, keysets);
4044
rules.forEach(rule -> ruleToFuncMap.put(rule, PseudoFuncFactory.create(ruleToPseudoFuncConfigs.get(rule))));
4145
}
4246

4347
// TODO: Move these init functions elsewhere?
44-
static Map<PseudoFuncRule, PseudoFuncConfig> initPseudoFuncConfigs(Collection<PseudoFuncRule> pseudoRules,
48+
Map<PseudoFuncRule, PseudoFuncConfig> initPseudoFuncConfigs(Collection<PseudoFuncRule> pseudoRules,
4549
Collection<PseudoSecret> pseudoSecrets,
4650
Collection<PseudoKeyset> pseudoKeysets) {
4751

@@ -70,7 +74,7 @@ static Map<PseudoFuncRule, PseudoFuncConfig> initPseudoFuncConfigs(Collection<Ps
7074
}));
7175
}
7276

73-
private static void enrichMapAndEncryptFunc(PseudoFuncConfig funcConfig,
77+
private void enrichMapAndEncryptFunc(PseudoFuncConfig funcConfig,
7478
Map<String, PseudoKeyset> pseudoKeysetMap,
7579
Map<String, PseudoSecret> pseudoSecretsMap,
7680
Collection<PseudoSecret> pseudoSecrets) {
@@ -86,15 +90,15 @@ private static void enrichMapAndEncryptFunc(PseudoFuncConfig funcConfig,
8690
}
8791
}
8892

89-
private static void enrichLegacyFpeFuncConfig(PseudoFuncConfig funcConfig, Map<String, PseudoSecret> pseudoSecretsMap) {
93+
private void enrichLegacyFpeFuncConfig(PseudoFuncConfig funcConfig, Map<String, PseudoSecret> pseudoSecretsMap) {
9094
String secretId = funcConfig.getRequired(FpeFuncConfig.Param.KEY_ID, String.class);
9195
if (! pseudoSecretsMap.containsKey(secretId)) {
9296
throw new PseudoException("No secret found for FPE pseudo func with " + FpeFuncConfig.Param.KEY_ID + "=" + secretId);
9397
}
9498
funcConfig.add(FpeFuncConfig.Param.KEY_DATA, pseudoSecretsMap.get(secretId).getBase64EncodedContent());
9599
}
96100

97-
private static void enrichTinkDaeadFuncConfig(PseudoFuncConfig funcConfig, Map<String, PseudoKeyset> keysetMap, Collection<PseudoSecret> pseudoSecrets) {
101+
private void enrichTinkDaeadFuncConfig(PseudoFuncConfig funcConfig, Map<String, PseudoKeyset> keysetMap, Collection<PseudoSecret> pseudoSecrets) {
98102
String dekId = funcConfig.getRequired(TinkDaeadFuncConfig.Param.KEY_ID, String.class);
99103

100104
// TODO: Support creating new key material instead of failing if none found?
@@ -116,9 +120,13 @@ private static void enrichTinkDaeadFuncConfig(PseudoFuncConfig funcConfig, Map<S
116120

117121
try {
118122
String keyUri = keyset.getKekUri().toString();
123+
124+
Aead masterKey = Optional.ofNullable(this.aeadCache.get(keyUri))
125+
.orElseThrow(() -> new PseudoFuncException("Key material with URI " + keyUri + " not found in cache"));
126+
119127
KeysetHandle keysetHandle = KeysetHandle.read(
120128
JsonKeysetReader.withString(keyset.toJson()),
121-
KmsClients.get(keyUri).getAead(keyUri)
129+
masterKey
122130
);
123131

124132
DeterministicAead daead = keysetHandle.getPrimitive(DeterministicAead.class);
@@ -129,7 +137,7 @@ private static void enrichTinkDaeadFuncConfig(PseudoFuncConfig funcConfig, Map<S
129137
}
130138
}
131139

132-
private static void enrichTinkFpeFuncConfig(PseudoFuncConfig funcConfig, Map<String, PseudoKeyset> keysetMap, Collection<PseudoSecret> pseudoSecrets) {
140+
private void enrichTinkFpeFuncConfig(PseudoFuncConfig funcConfig, Map<String, PseudoKeyset> keysetMap, Collection<PseudoSecret> pseudoSecrets) {
133141
String dekId = funcConfig.getRequired(TinkFpeFuncConfig.Param.KEY_ID, String.class);
134142

135143
// TODO: Support creating new key material instead of failing if none found?
@@ -151,9 +159,13 @@ private static void enrichTinkFpeFuncConfig(PseudoFuncConfig funcConfig, Map<Str
151159

152160
try {
153161
String keyUri = keyset.getKekUri().toString();
162+
163+
Aead masterKey = Optional.ofNullable(this.aeadCache.get(keyUri))
164+
.orElseThrow(() -> new PseudoFuncException("Key material with URI " + keyUri + " not found in cache"));
165+
154166
KeysetHandle keysetHandle = KeysetHandle.read(
155167
JsonKeysetReader.withString(keyset.toJson()),
156-
KmsClients.get(keyUri).getAead(keyUri)
168+
masterKey
157169
);
158170

159171
Fpe fpe = keysetHandle.getPrimitive(Fpe.class);

0 commit comments

Comments
 (0)