diff --git a/conf/default-config.json b/conf/default-config.json
index 224df8906..d4408bc34 100644
--- a/conf/default-config.json
+++ b/conf/default-config.json
@@ -30,6 +30,8 @@
"salts_metadata_path": "salts/metadata.json",
"services_metadata_path": "services/metadata.json",
"service_links_metadata_path": "service_links/metadata.json",
+ "cloud_encryption_keys_metadata_path": "cloud_encryption_keys/metadata.json",
+ "cloud_encryption_keys_refresh_ms": 300000,
"optout_metadata_path": null,
"optout_inmem_cache": false,
"enclave_platform": null,
diff --git a/conf/docker-config.json b/conf/docker-config.json
index 25f38e6ae..8afd14869 100644
--- a/conf/docker-config.json
+++ b/conf/docker-config.json
@@ -31,6 +31,7 @@
"salts_metadata_path": "/com.uid2.core/test/salts/metadata.json",
"services_metadata_path": "/com.uid2.core/test/services/metadata.json",
"service_links_metadata_path": "/com.uid2.core/test/service_links/metadata.json",
+ "cloud_encryption_keys_metadata_path": "/com.uid2.core/test/cloud_encryption_keys/metadata.json",
"identity_token_expires_after_seconds": 3600,
"optout_metadata_path": null,
"optout_inmem_cache": false,
diff --git a/conf/integ-config.json b/conf/integ-config.json
index f1cf90742..3efa3fc7a 100644
--- a/conf/integ-config.json
+++ b/conf/integ-config.json
@@ -14,6 +14,6 @@
"optout_api_token": "test-operator-key",
"optout_api_uri": "http://localhost:8081/optout/replicate",
"salts_expired_shutdown_hours": 12,
+ "cloud_encryption_keys_metadata_path": "http://localhost:8088/cloud_encryption_keys/retrieve",
"operator_type": "public"
-
}
\ No newline at end of file
diff --git a/conf/local-config.json b/conf/local-config.json
index 6a357dba1..d1bf225b5 100644
--- a/conf/local-config.json
+++ b/conf/local-config.json
@@ -9,6 +9,7 @@
"salts_metadata_path": "/com.uid2.core/test/salts/metadata.json",
"services_metadata_path": "/com.uid2.core/test/services/metadata.json",
"service_links_metadata_path": "/com.uid2.core/test/service_links/metadata.json",
+ "cloud_encryption_keys_metadata_path":"/com.uid2.core/test/cloud_encryption_keys/metadata.json",
"identity_token_expires_after_seconds": 3600,
"refresh_token_expires_after_seconds": 86400,
"refresh_identity_token_after_seconds": 900,
diff --git a/conf/local-e2e-docker-private-config.json b/conf/local-e2e-docker-private-config.json
index 8637e6da3..084e05004 100644
--- a/conf/local-e2e-docker-private-config.json
+++ b/conf/local-e2e-docker-private-config.json
@@ -11,6 +11,7 @@
"keysets_metadata_path": "http://core:8088/key/keyset/refresh",
"keyset_keys_metadata_path": "http://core:8088/key/keyset-keys/refresh",
"salts_metadata_path": "http://core:8088/salt/refresh",
+ "cloud_encryption_keys_metadata_path": "http://core:8088/cloud_encryption_keys/retrieve",
"identity_token_expires_after_seconds": 3600,
"refresh_token_expires_after_seconds": 86400,
"refresh_identity_token_after_seconds": 900,
diff --git a/conf/local-e2e-docker-public-config.json b/conf/local-e2e-docker-public-config.json
index a145c4d17..49c07e2aa 100644
--- a/conf/local-e2e-docker-public-config.json
+++ b/conf/local-e2e-docker-public-config.json
@@ -13,6 +13,7 @@
"salts_metadata_path": "http://core:8088/salt/refresh",
"services_metadata_path": "http://core:8088/services/refresh",
"service_links_metadata_path": "http://core:8088/service_links/refresh",
+ "cloud_encryption_keys_metadata_path": "http://core:8088/cloud_encryption_keys/retrieve",
"identity_token_expires_after_seconds": 3600,
"refresh_token_expires_after_seconds": 86400,
"refresh_identity_token_after_seconds": 900,
diff --git a/conf/local-e2e-private-config.json b/conf/local-e2e-private-config.json
index 4ab52330f..3dc31bfdf 100644
--- a/conf/local-e2e-private-config.json
+++ b/conf/local-e2e-private-config.json
@@ -13,6 +13,7 @@
"salts_metadata_path": "http://localhost:8088/salt/refresh",
"services_metadata_path": "http://localhost:8088/services/refresh",
"service_links_metadata_path": "http://localhost:8088/service_links/refresh",
+ "cloud_encryption_keys_metadata_path": "http://core:8088/cloud_encryption_keys/retrieve",
"identity_token_expires_after_seconds": 3600,
"refresh_token_expires_after_seconds": 86400,
"refresh_identity_token_after_seconds": 900,
diff --git a/conf/local-e2e-public-config.json b/conf/local-e2e-public-config.json
index bfdc8e394..3b591de62 100644
--- a/conf/local-e2e-public-config.json
+++ b/conf/local-e2e-public-config.json
@@ -13,6 +13,7 @@
"salts_metadata_path": "http://localhost:8088/salt/refresh",
"services_metadata_path": "http://localhost:8088/services/refresh",
"service_links_metadata_path": "http://localhost:8088/service_links/refresh",
+ "cloud_encryption_keys_metadata_path": "http://core:8088/cloud_encryption_keys/retrieve",
"identity_token_expires_after_seconds": 3600,
"refresh_token_expires_after_seconds": 86400,
"refresh_identity_token_after_seconds": 900,
diff --git a/conf/validator-latest-e2e-docker-public-config.json b/conf/validator-latest-e2e-docker-public-config.json
index 8f82b01a4..f607201c6 100644
--- a/conf/validator-latest-e2e-docker-public-config.json
+++ b/conf/validator-latest-e2e-docker-public-config.json
@@ -14,6 +14,7 @@
"salts_metadata_path": "http://core:8088/salt/refresh",
"services_metadata_path": "http://core:8088/services/refresh",
"service_links_metadata_path": "http://core:8088/service_links/refresh",
+ "cloud_encryption_keys_metadata_path": "https://core:8088/cloud_encryption_keys/retrieve",
"identity_token_expires_after_seconds": 3600,
"refresh_token_expires_after_seconds": 86400,
"refresh_identity_token_after_seconds": 900,
diff --git a/pom.xml b/pom.xml
index 69634018d..c11f47781 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,7 +6,7 @@
com.uid2
uid2-operator
- 5.43.4
+ 5.43.6-alpha-147-SNAPSHOT
UTF-8
@@ -22,7 +22,7 @@
2.1.0
2.1.0
2.1.0
- 8.0.9
+ 8.0.15-alpha-177-SNAPSHOT
${project.version}
21
21
diff --git a/scripts/aws/conf/integ-euid-config.json b/scripts/aws/conf/integ-euid-config.json
index 45d3dbe94..22f8a15e7 100644
--- a/scripts/aws/conf/integ-euid-config.json
+++ b/scripts/aws/conf/integ-euid-config.json
@@ -9,6 +9,7 @@
"service_links_metadata_path": "https://core.integ.euid.eu/service_links/refresh",
"optout_metadata_path": "https://optout.integ.euid.eu/optout/refresh",
"core_attest_url": "https://core.integ.euid.eu/attest",
+ "cloud_encryption_keys_metadata_path": "https://core.integ.euid.eu/cloud_encryption_keys/retrieve",
"optout_api_uri": "https://optout.integ.euid.eu/optout/replicate",
"optout_s3_folder": "optout/",
"allow_legacy_api": false
diff --git a/scripts/aws/conf/integ-uid2-config.json b/scripts/aws/conf/integ-uid2-config.json
index a7272a26a..11b0d6048 100644
--- a/scripts/aws/conf/integ-uid2-config.json
+++ b/scripts/aws/conf/integ-uid2-config.json
@@ -8,6 +8,7 @@
"services_metadata_path": "https://core-integ.uidapi.com/services/refresh",
"service_links_metadata_path": "https://core-integ.uidapi.com/service_links/refresh",
"optout_metadata_path": "https://optout-integ.uidapi.com/optout/refresh",
+ "cloud_encryption_keys_metadata_path": "https://core-integ.uidapi.com/cloud_encryption_keys/retrieve",
"core_attest_url": "https://core-integ.uidapi.com/attest",
"optout_api_uri": "https://optout-integ.uidapi.com/optout/replicate",
"optout_s3_folder": "uid-optout-integ/",
diff --git a/scripts/aws/conf/prod-euid-config.json b/scripts/aws/conf/prod-euid-config.json
index 0fbf5d69c..7f7ecdef4 100644
--- a/scripts/aws/conf/prod-euid-config.json
+++ b/scripts/aws/conf/prod-euid-config.json
@@ -9,6 +9,7 @@
"services_metadata_path": "https://core.prod.euid.eu/services/refresh",
"service_links_metadata_path": "https://core.prod.euid.eu/service_links/refresh",
"optout_metadata_path": "https://optout.prod.euid.eu/optout/refresh",
+ "cloud_encryption_keys_metadata_path": "https://core.prod.euid.eu/cloud_encryption_keys/retrieve",
"core_attest_url": "https://core.prod.euid.eu/attest",
"core_api_token": "your-api-token",
"optout_s3_path_compat": false,
diff --git a/scripts/aws/conf/prod-uid2-config.json b/scripts/aws/conf/prod-uid2-config.json
index 5da450033..8dd9b63d5 100644
--- a/scripts/aws/conf/prod-uid2-config.json
+++ b/scripts/aws/conf/prod-uid2-config.json
@@ -8,6 +8,7 @@
"salts_metadata_path": "https://core-prod.uidapi.com/salt/refresh",
"services_metadata_path": "https://core-prod.uidapi.com/services/refresh",
"service_links_metadata_path": "https://core-prod.uidapi.com/service_links/refresh",
+ "cloud_encryption_keys_metadata_path": "https://core-prod.uidapi.com/cloud_encryption_keys/retrieve",
"optout_metadata_path": "https://optout-prod.uidapi.com/optout/refresh",
"core_attest_url": "https://core-prod.uidapi.com/attest",
"core_api_token": "your-api-token",
diff --git a/scripts/azure-cc/conf/integ-uid2-config.json b/scripts/azure-cc/conf/integ-uid2-config.json
index 2cd4be5c3..f75f3717d 100644
--- a/scripts/azure-cc/conf/integ-uid2-config.json
+++ b/scripts/azure-cc/conf/integ-uid2-config.json
@@ -7,6 +7,7 @@
"salts_metadata_path": "https://core-integ.uidapi.com/salt/refresh",
"services_metadata_path": "https://core-integ.uidapi.com/services/refresh",
"service_links_metadata_path": "https://core-integ.uidapi.com/service_links/refresh",
+ "cloud_encryption_keys_metadata_path": "https://core-integ.uidapi.com/cloud_encryption_keys/retrieve",
"optout_metadata_path": "https://optout-integ.uidapi.com/optout/refresh",
"core_attest_url": "https://core-integ.uidapi.com/attest",
"optout_api_uri": "https://optout-integ.uidapi.com/optout/replicate",
diff --git a/scripts/azure-cc/conf/prod-uid2-config.json b/scripts/azure-cc/conf/prod-uid2-config.json
index 02e2cde20..3703337a4 100644
--- a/scripts/azure-cc/conf/prod-uid2-config.json
+++ b/scripts/azure-cc/conf/prod-uid2-config.json
@@ -7,6 +7,7 @@
"salts_metadata_path": "https://core-prod.uidapi.com/salt/refresh",
"services_metadata_path": "https://core-prod.uidapi.com/services/refresh",
"service_links_metadata_path": "https://core-prod.uidapi.com/service_links/refresh",
+ "cloud_encryption_keys_metadata_path": "https://core-prod.uidapi.com/cloud_encryption_keys/retrieve",
"optout_metadata_path": "https://optout-prod.uidapi.com/optout/refresh",
"core_attest_url": "https://core-prod.uidapi.com/attest",
"optout_api_uri": "https://optout-prod.uidapi.com/optout/replicate",
diff --git a/scripts/gcp/conf/integ-config.json b/scripts/gcp/conf/integ-config.json
index d3fb9e9ff..09e93dfcc 100644
--- a/scripts/gcp/conf/integ-config.json
+++ b/scripts/gcp/conf/integ-config.json
@@ -5,6 +5,7 @@
"salts_metadata_path": "https://core-integ.uidapi.com/salt/refresh",
"core_attest_url": "https://core-integ.uidapi.com/attest",
"optout_metadata_path": "https://optout-integ.uidapi.com/optout/refresh",
+ "cloud_encryption_keys_metadata_path": "https://core-integ.uidapi.com/cloud_encryption_keys/retrieve",
"optout_api_uri": "https://optout-integ.uidapi.com/optout/replicate",
"optout_s3_folder": "optout-v2/",
"optout_inmem_cache": true,
diff --git a/scripts/gcp/conf/prod-config.json b/scripts/gcp/conf/prod-config.json
index 836349c19..32bc0085c 100644
--- a/scripts/gcp/conf/prod-config.json
+++ b/scripts/gcp/conf/prod-config.json
@@ -6,6 +6,7 @@
"core_attest_url": "https://core-prod.uidapi.com/attest",
"optout_metadata_path": "https://optout-prod.uidapi.com/optout/refresh",
"optout_api_uri": "https://optout-prod.uidapi.com/optout/replicate",
+ "cloud_encryption_keys_metadata_path": "https://core-prod.uidapi.com/cloud_encryption_keys/retrieve",
"optout_s3_folder": "optout-v2/",
"optout_inmem_cache": true,
"identity_token_expires_after_seconds": 14400,
diff --git a/src/main/java/com/uid2/operator/Main.java b/src/main/java/com/uid2/operator/Main.java
index 6b88d715b..04643d21e 100644
--- a/src/main/java/com/uid2/operator/Main.java
+++ b/src/main/java/com/uid2/operator/Main.java
@@ -8,6 +8,7 @@
import com.uid2.operator.monitoring.IStatsCollectorQueue;
import com.uid2.operator.monitoring.OperatorMetrics;
import com.uid2.operator.monitoring.StatsCollectorVerticle;
+import com.uid2.operator.reader.RotatingCloudEncryptionKeyApiProvider;
import com.uid2.operator.service.SecureLinkValidatorService;
import com.uid2.operator.service.ShutdownService;
import com.uid2.operator.vertx.Endpoints;
@@ -22,6 +23,7 @@
import com.uid2.shared.jmx.AdminApi;
import com.uid2.shared.optout.OptOutCloudSync;
import com.uid2.shared.store.CloudPath;
+import com.uid2.shared.store.EncryptedRotatingSaltProvider;
import com.uid2.shared.store.RotatingSaltProvider;
import com.uid2.shared.store.reader.*;
import com.uid2.shared.store.scope.GlobalScope;
@@ -81,6 +83,7 @@ public class Main {
private IStatsCollectorQueue _statsCollectorQueue;
private RotatingServiceStore serviceProvider;
private RotatingServiceLinkStore serviceLinkProvider;
+ private RotatingCloudEncryptionKeyApiProvider cloudEncryptionKeyProvider;
public Main(Vertx vertx, JsonObject config) throws Exception {
this.vertx = vertx;
@@ -132,17 +135,19 @@ public Main(Vertx vertx, JsonObject config) throws Exception {
this.fsOptOut = configureCloudOptOutStore();
}
+ String cloudEncryptionKeyMdPath = this.config.getString(Const.Config.CloudEncryptionKeysMetadataPathProp);
+ this.cloudEncryptionKeyProvider = new RotatingCloudEncryptionKeyApiProvider(fsStores, new GlobalScope(new CloudPath(cloudEncryptionKeyMdPath)));
String sitesMdPath = this.config.getString(Const.Config.SitesMetadataPathProp);
String keypairMdPath = this.config.getString(Const.Config.ClientSideKeypairsMetadataPathProp);
- this.clientSideKeypairProvider = new RotatingClientSideKeypairStore(fsStores, new GlobalScope(new CloudPath(keypairMdPath)));
+ this.clientSideKeypairProvider = new RotatingClientSideKeypairStore(fsStores, new GlobalScope(new CloudPath(keypairMdPath)), cloudEncryptionKeyProvider);
String clientsMdPath = this.config.getString(Const.Config.ClientsMetadataPathProp);
- this.clientKeyProvider = new RotatingClientKeyProvider(fsStores, new GlobalScope(new CloudPath(clientsMdPath)));
+ this.clientKeyProvider = new RotatingClientKeyProvider(fsStores, new GlobalScope(new CloudPath(clientsMdPath)), cloudEncryptionKeyProvider);
String keysetKeysMdPath = this.config.getString(Const.Config.KeysetKeysMetadataPathProp);
- this.keysetKeyStore = new RotatingKeysetKeyStore(fsStores, new GlobalScope(new CloudPath(keysetKeysMdPath)));
+ this.keysetKeyStore = new RotatingKeysetKeyStore(fsStores, new GlobalScope(new CloudPath(keysetKeysMdPath)), cloudEncryptionKeyProvider);
String keysetMdPath = this.config.getString(Const.Config.KeysetsMetadataPathProp);
- this.keysetProvider = new RotatingKeysetProvider(fsStores, new GlobalScope(new CloudPath(keysetMdPath)));
+ this.keysetProvider = new RotatingKeysetProvider(fsStores, new GlobalScope(new CloudPath(keysetMdPath)), cloudEncryptionKeyProvider);
String saltsMdPath = this.config.getString(Const.Config.SaltsMetadataPathProp);
- this.saltProvider = new RotatingSaltProvider(fsStores, saltsMdPath);
+ this.saltProvider = new EncryptedRotatingSaltProvider(fsStores, cloudEncryptionKeyProvider, new GlobalScope(new CloudPath(saltsMdPath)));
this.optOutStore = new CloudSyncOptOutStore(vertx, fsLocal, this.config, operatorKey, Clock.systemUTC());
if (this.validateServiceLinks) {
@@ -152,7 +157,7 @@ public Main(Vertx vertx, JsonObject config) throws Exception {
this.serviceLinkProvider = new RotatingServiceLinkStore(fsStores, new GlobalScope(new CloudPath(serviceLinkMdPath)));
}
- this.siteProvider = clientSideTokenGenerate ? new RotatingSiteStore(fsStores, new GlobalScope(new CloudPath(sitesMdPath))) : null;
+ this.siteProvider = clientSideTokenGenerate ? new RotatingSiteStore(fsStores, new GlobalScope(new CloudPath(sitesMdPath)), cloudEncryptionKeyProvider) : null;
if (useStorageMock && coreAttestUrl == null) {
if (clientSideTokenGenerate) {
@@ -163,6 +168,7 @@ public Main(Vertx vertx, JsonObject config) throws Exception {
this.saltProvider.loadContent();
this.keysetProvider.loadContent();
this.keysetKeyStore.loadContent();
+ this.cloudEncryptionKeyProvider.loadContent();
if (this.validateServiceLinks) {
this.serviceProvider.loadContent();
@@ -302,6 +308,8 @@ private void run() throws Exception {
private Future createStoreVerticles() throws Exception {
// load metadatas for the first time
+ cloudEncryptionKeyProvider.loadContent();
+
if (clientSideTokenGenerate) {
siteProvider.getMetadata();
clientSideKeypairProvider.getMetadata();
@@ -330,6 +338,7 @@ private Future createStoreVerticles() throws Exception {
fs.add(createAndDeployRotatingStoreVerticle("auth", clientKeyProvider, "auth_refresh_ms"));
fs.add(createAndDeployRotatingStoreVerticle("keyset", keysetProvider, "keyset_refresh_ms"));
fs.add(createAndDeployRotatingStoreVerticle("keysetkey", keysetKeyStore, "keysetkey_refresh_ms"));
+ fs.add(createAndDeployRotatingStoreVerticle("cloud_encryption_keys", cloudEncryptionKeyProvider, "cloud_encryption_keys_refresh_ms"));
fs.add(createAndDeployRotatingStoreVerticle("salt", saltProvider, "salt_refresh_ms"));
fs.add(createAndDeployCloudSyncStoreVerticle("optout", fsOptOut, optOutCloudSync));
CompositeFuture.all(fs).onComplete(ar -> {
diff --git a/src/main/java/com/uid2/operator/reader/ApiStoreReader.java b/src/main/java/com/uid2/operator/reader/ApiStoreReader.java
new file mode 100644
index 000000000..fda4c4e6a
--- /dev/null
+++ b/src/main/java/com/uid2/operator/reader/ApiStoreReader.java
@@ -0,0 +1,57 @@
+package com.uid2.operator.reader;
+
+import com.uid2.shared.cloud.DownloadCloudStorage;
+import com.uid2.shared.store.ScopedStoreReader;
+import com.uid2.shared.store.parser.Parser;
+import com.uid2.shared.store.parser.ParsingResult;
+import com.uid2.shared.store.scope.StoreScope;
+import io.vertx.core.json.JsonArray;
+import io.vertx.core.json.JsonObject;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+
+public class ApiStoreReader extends ScopedStoreReader {
+ private static final Logger LOGGER = LoggerFactory.getLogger(ApiStoreReader.class);
+
+ public ApiStoreReader(DownloadCloudStorage fileStreamProvider, StoreScope scope, Parser parser, String dataTypeName) {
+ super(fileStreamProvider, scope, parser, dataTypeName);
+ }
+
+
+ public long loadContent(JsonObject contents) throws Exception {
+ return loadContent(contents, dataTypeName);
+ }
+
+ @Override
+ public long loadContent(JsonObject contents, String dataType) throws IOException {
+ if (contents == null) {
+ throw new IllegalArgumentException(String.format("No contents provided for loading data type %s, cannot load content", dataType));
+ }
+
+ try {
+ JsonArray dataArray = contents.getJsonArray(dataType);
+ if (dataArray == null) {
+ throw new IllegalArgumentException("No array found in the contents");
+ }
+
+ String jsonString = dataArray.toString();
+ InputStream inputStream = new ByteArrayInputStream(jsonString.getBytes(StandardCharsets.UTF_8));
+
+ ParsingResult parsed = parser.deserialize(inputStream);
+ latestSnapshot.set(parsed.getData());
+
+ final int count = parsed.getCount();
+ latestEntryCount.set(count);
+ LOGGER.info(String.format("Loaded %d %s", count, dataType));
+ return count;
+ } catch (Exception e) {
+ LOGGER.error(String.format("Unable to load %s", dataType));
+ throw e;
+ }
+ }
+}
diff --git a/src/main/java/com/uid2/operator/reader/RotatingCloudEncryptionKeyApiProvider.java b/src/main/java/com/uid2/operator/reader/RotatingCloudEncryptionKeyApiProvider.java
new file mode 100644
index 000000000..2b4d1b10d
--- /dev/null
+++ b/src/main/java/com/uid2/operator/reader/RotatingCloudEncryptionKeyApiProvider.java
@@ -0,0 +1,32 @@
+package com.uid2.operator.reader;
+
+import com.uid2.shared.cloud.DownloadCloudStorage;
+import com.uid2.shared.model.CloudEncryptionKey;
+import com.uid2.shared.store.CloudPath;
+import com.uid2.shared.store.parser.CloudEncryptionKeyParser;
+import com.uid2.shared.store.reader.RotatingCloudEncryptionKeyProvider;
+import com.uid2.shared.store.scope.StoreScope;
+import io.vertx.core.json.JsonObject;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.time.Instant;
+import java.util.*;
+
+public class RotatingCloudEncryptionKeyApiProvider extends RotatingCloudEncryptionKeyProvider {
+ private static final Logger LOGGER = LoggerFactory.getLogger(RotatingCloudEncryptionKeyApiProvider.class);
+
+ public RotatingCloudEncryptionKeyApiProvider(DownloadCloudStorage fileStreamProvider, StoreScope scope) {
+ super(fileStreamProvider, scope, new ApiStoreReader<>(fileStreamProvider, scope, new CloudEncryptionKeyParser(), "cloud_encryption_keys"));
+ }
+
+ public RotatingCloudEncryptionKeyApiProvider(DownloadCloudStorage fileStreamProvider, StoreScope scope, ApiStoreReader