From 36235b0f4675adea1499ee859899647dbe59cf91 Mon Sep 17 00:00:00 2001 From: Cody Constine Date: Fri, 6 Dec 2024 15:54:45 -0700 Subject: [PATCH] Adding more work on salt encryption --- pom.xml | 2 +- .../EncryptionJob/ClientKeyEncryptionJob.java | 2 +- .../job/EncryptionJob/SaltEncryptionJob.java | 39 ++++++++++ .../job/jobsync/EncryptedFilesSyncJob.java | 2 - .../admin/store/factory/SaltStoreFactory.java | 66 ++++++++++++++++ .../store/writer/EncyptedSaltStoreWriter.java | 75 +++++++++++++++++++ .../admin/store/writer/SaltStoreWriter.java | 12 ++- .../com/uid2/admin/util/PrivateSiteUtil.java | 15 ++++ .../com/uid2/admin/util/PublicSiteUtil.java | 15 ++++ 9 files changed, 221 insertions(+), 7 deletions(-) create mode 100644 src/main/java/com/uid2/admin/job/EncryptionJob/SaltEncryptionJob.java create mode 100644 src/main/java/com/uid2/admin/store/factory/SaltStoreFactory.java create mode 100644 src/main/java/com/uid2/admin/store/writer/EncyptedSaltStoreWriter.java diff --git a/pom.xml b/pom.xml index 479eb475..d8e2d5f4 100644 --- a/pom.xml +++ b/pom.xml @@ -16,7 +16,7 @@ 1.12.2 5.11.2 - 8.0.6 + 8.0.10-alpha-172-SNAPSHOT 0.5.10 ${project.version} diff --git a/src/main/java/com/uid2/admin/job/EncryptionJob/ClientKeyEncryptionJob.java b/src/main/java/com/uid2/admin/job/EncryptionJob/ClientKeyEncryptionJob.java index 9febe5f7..8e81705c 100644 --- a/src/main/java/com/uid2/admin/job/EncryptionJob/ClientKeyEncryptionJob.java +++ b/src/main/java/com/uid2/admin/job/EncryptionJob/ClientKeyEncryptionJob.java @@ -33,7 +33,7 @@ public String getId() { public void execute() throws Exception { PrivateSiteDataMap desiredPrivateState = PrivateSiteUtil.getClientKeys(globalOperators, globalClientKeys); multiScopeStoreWriter.uploadPrivateWithEncryption(desiredPrivateState, null); - PrivateSiteDataMap desiredPublicState = PublicSiteUtil.getPublicClients(globalClientKeys,globalOperators); + PrivateSiteDataMap desiredPublicState = PublicSiteUtil.getPublicClients(globalClientKeys, globalOperators); multiScopeStoreWriter.uploadPublicWithEncryption(desiredPublicState, null); } } diff --git a/src/main/java/com/uid2/admin/job/EncryptionJob/SaltEncryptionJob.java b/src/main/java/com/uid2/admin/job/EncryptionJob/SaltEncryptionJob.java new file mode 100644 index 00000000..988b17da --- /dev/null +++ b/src/main/java/com/uid2/admin/job/EncryptionJob/SaltEncryptionJob.java @@ -0,0 +1,39 @@ +package com.uid2.admin.job.EncryptionJob; + +import com.uid2.admin.job.model.Job; +import com.uid2.admin.model.PrivateSiteDataMap; +import com.uid2.admin.store.MultiScopeStoreWriter; +import com.uid2.admin.util.PrivateSiteUtil; +import com.uid2.admin.util.PublicSiteUtil; +import com.uid2.shared.auth.OperatorKey; +import com.uid2.shared.model.SaltEntry; + +import java.util.Collection; + +public class SaltEncryptionJob extends Job { + private final Collection globalOperators; + private final Collection saltEntries; + private final MultiScopeStoreWriter> multiScopeStoreWriter; + + public SaltEncryptionJob(Collection globalOperators, + Collection saltEntries, + MultiScopeStoreWriter> multiScopeStoreWriter) { + this.globalOperators = globalOperators; + this.saltEntries = saltEntries; + this.multiScopeStoreWriter = multiScopeStoreWriter; + } + + + @Override + public String getId() { + return "cloud-encryption-sync-salts"; + } + + @Override + public void execute() throws Exception { + PrivateSiteDataMap desiredPrivateState = PrivateSiteUtil.getPrivateSaltEntries(saltEntries, globalOperators); + multiScopeStoreWriter.uploadPrivateWithEncryption(desiredPrivateState, null); + PrivateSiteDataMap desiredPublicState = PublicSiteUtil.getPublicSaltEntries(saltEntries, globalOperators); + multiScopeStoreWriter.uploadPublicWithEncryption(desiredPublicState, null); + } +} diff --git a/src/main/java/com/uid2/admin/job/jobsync/EncryptedFilesSyncJob.java b/src/main/java/com/uid2/admin/job/jobsync/EncryptedFilesSyncJob.java index 14077ac3..31c87b3e 100644 --- a/src/main/java/com/uid2/admin/job/jobsync/EncryptedFilesSyncJob.java +++ b/src/main/java/com/uid2/admin/job/jobsync/EncryptedFilesSyncJob.java @@ -2,7 +2,6 @@ import com.fasterxml.jackson.databind.ObjectWriter; import com.uid2.admin.job.EncryptionJob.*; -import com.uid2.admin.job.EncryptionJob.ClientKeyEncryptionJob; import com.uid2.admin.job.model.Job; import com.uid2.admin.store.*; import com.uid2.admin.store.factory.*; @@ -23,7 +22,6 @@ import com.uid2.shared.store.CloudPath; import com.uid2.admin.legacy.LegacyClientKey; import com.uid2.shared.store.reader.RotatingCloudEncryptionKeyProvider; -import com.uid2.shared.store.reader.RotatingCloudEncryptionKeyProvider; import com.uid2.shared.store.scope.GlobalScope; import io.vertx.core.json.JsonObject; diff --git a/src/main/java/com/uid2/admin/store/factory/SaltStoreFactory.java b/src/main/java/com/uid2/admin/store/factory/SaltStoreFactory.java new file mode 100644 index 00000000..21f31a1e --- /dev/null +++ b/src/main/java/com/uid2/admin/store/factory/SaltStoreFactory.java @@ -0,0 +1,66 @@ +package com.uid2.admin.store.factory; + +import com.uid2.admin.store.FileManager; +import com.uid2.admin.store.version.VersionGenerator; +import com.uid2.admin.store.writer.EncyptedSaltStoreWriter; +import com.uid2.admin.store.writer.StoreWriter; +import com.uid2.shared.cloud.TaggableCloudStorage; +import com.uid2.shared.store.CloudPath; +import com.uid2.shared.store.RotatingEncryptedSaltProvider; +import com.uid2.shared.store.RotatingSaltProvider; +import com.uid2.shared.store.reader.RotatingCloudEncryptionKeyProvider; +import com.uid2.shared.store.reader.StoreReader; +import com.uid2.shared.store.scope.EncryptedScope; +import io.vertx.core.json.JsonObject; + +import java.util.Collection; + +public class SaltStoreFactory implements EncryptedStoreFactory> { + JsonObject config; + CloudPath rootMetadatapath; + RotatingSaltProvider saltProvider; + FileManager fileManager; + TaggableCloudStorage taggableCloudStorage; + VersionGenerator versionGenerator; + RotatingCloudEncryptionKeyProvider cloudEncryptionKeyProvider; + + public SaltStoreFactory(JsonObject config, CloudPath rootMetadataPath, RotatingSaltProvider saltProvider, FileManager fileManager, + TaggableCloudStorage taggableCloudStorage, VersionGenerator versionGenerator, + RotatingCloudEncryptionKeyProvider cloudEncryptionKeyProvider) { + this.config = config; + this.rootMetadatapath = rootMetadataPath; + this.saltProvider = saltProvider; + this.fileManager = fileManager; + this.taggableCloudStorage = taggableCloudStorage; + this.versionGenerator = versionGenerator; + this.cloudEncryptionKeyProvider = cloudEncryptionKeyProvider; + } + + @Override + public StoreWriter> getEncryptedWriter(Integer siteId, boolean isPublic) { + return new EncyptedSaltStoreWriter(config, saltProvider, fileManager, taggableCloudStorage, versionGenerator, + new EncryptedScope(rootMetadatapath, siteId, isPublic), cloudEncryptionKeyProvider, siteId); + } + + @Override + public StoreReader> getEncryptedReader(Integer siteId, boolean isPublic) { + return new RotatingEncryptedSaltProvider(taggableCloudStorage, + new EncryptedScope(rootMetadatapath, siteId, isPublic).getMetadataPath().toString(), + cloudEncryptionKeyProvider); + } + + @Override + public RotatingCloudEncryptionKeyProvider getCloudEncryptionProvider() { + return cloudEncryptionKeyProvider; + } + + @Override + public StoreReader> getReader(Integer siteId) { + return null; + } + + @Override + public StoreWriter> getWriter(Integer siteId) { + return null; + } +} diff --git a/src/main/java/com/uid2/admin/store/writer/EncyptedSaltStoreWriter.java b/src/main/java/com/uid2/admin/store/writer/EncyptedSaltStoreWriter.java new file mode 100644 index 00000000..d29f8a29 --- /dev/null +++ b/src/main/java/com/uid2/admin/store/writer/EncyptedSaltStoreWriter.java @@ -0,0 +1,75 @@ +package com.uid2.admin.store.writer; + +import com.uid2.admin.store.FileManager; +import com.uid2.admin.store.version.VersionGenerator; +import com.uid2.shared.cloud.TaggableCloudStorage; +import com.uid2.shared.encryption.AesGcm; +import com.uid2.shared.model.CloudEncryptionKey; +import com.uid2.shared.store.CloudPath; +import com.uid2.shared.store.RotatingSaltProvider; +import com.uid2.shared.store.reader.RotatingCloudEncryptionKeyProvider; +import com.uid2.shared.store.scope.StoreScope; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import io.vertx.core.json.JsonObject; + +import java.nio.charset.StandardCharsets; +import java.util.Base64; + +public class EncyptedSaltStoreWriter extends SaltStoreWriter implements StoreWriter { + private StoreScope scope; + private RotatingCloudEncryptionKeyProvider cloudEncryptionKeyProvider; + private Integer siteId; + + private static final Logger LOGGER = LoggerFactory.getLogger(EncyptedSaltStoreWriter.class); + public EncyptedSaltStoreWriter(JsonObject config, RotatingSaltProvider provider, FileManager fileManager, + TaggableCloudStorage cloudStorage, VersionGenerator versionGenerator, StoreScope scope, + RotatingCloudEncryptionKeyProvider cloudEncryptionKeyProvider, Integer siteId) { + super(config, provider, fileManager, cloudStorage, versionGenerator); + this.scope = scope; + this.cloudEncryptionKeyProvider = cloudEncryptionKeyProvider; + this.siteId = siteId; + } + + @Override + protected java.lang.String getSaltSnapshotLocation(RotatingSaltProvider.SaltSnapshot snapshot) { + return scope.resolve(new CloudPath(saltSnapshotLocationPrefix + snapshot.getEffective().toEpochMilli())).toString(); + } + + @Override + protected void upload(String data, String location) throws Exception { + if (siteId == null) { + throw new IllegalStateException("Site ID is not set."); + } + + CloudEncryptionKey encryptionKey = null; + try { + encryptionKey = cloudEncryptionKeyProvider.getEncryptionKeyForSite(siteId); + } catch (IllegalStateException e) { + LOGGER.error("Error: No Cloud Encryption keys available for encryption for site ID: {}", siteId, e); + } + JsonObject encryptedJson = new JsonObject(); + if (encryptionKey != null) { + byte[] secret = Base64.getDecoder().decode(encryptionKey.getSecret()); + byte[] encryptedPayload = AesGcm.encrypt(data.getBytes(StandardCharsets.UTF_8), secret); + encryptedJson.put("key_id", encryptionKey.getId()) + .put("encryption_version", "1.0") + .put("encrypted_payload", Base64.getEncoder().encodeToString(encryptedPayload)); + } else { + throw new IllegalStateException("No Cloud Encryption keys available for encryption for site ID: " + siteId); + } + + + super.upload(encryptedJson.encodePrettily(), location); + } + + @Override + public void upload(Object data, JsonObject extraMeta) throws Exception { + super.upload((RotatingSaltProvider.SaltSnapshot) data); + } + + @Override + public void rewriteMeta() throws Exception { + + } +} diff --git a/src/main/java/com/uid2/admin/store/writer/SaltStoreWriter.java b/src/main/java/com/uid2/admin/store/writer/SaltStoreWriter.java index 76626539..6d350527 100644 --- a/src/main/java/com/uid2/admin/store/writer/SaltStoreWriter.java +++ b/src/main/java/com/uid2/admin/store/writer/SaltStoreWriter.java @@ -26,7 +26,7 @@ public class SaltStoreWriter { private static final Logger LOGGER = LoggerFactory.getLogger(SaltStoreWriter.class); private final RotatingSaltProvider provider; private final FileManager fileManager; - private final String saltSnapshotLocationPrefix; + protected final String saltSnapshotLocationPrefix; private final VersionGenerator versionGenerator; private final TaggableCloudStorage cloudStorage; @@ -111,7 +111,7 @@ public void archiveSaltLocations() throws Exception { }); } - private String getSaltSnapshotLocation(RotatingSaltProvider.SaltSnapshot snapshot) { + protected String getSaltSnapshotLocation(RotatingSaltProvider.SaltSnapshot snapshot) { return saltSnapshotLocationPrefix + snapshot.getEffective().toEpochMilli(); } @@ -130,7 +130,13 @@ private void uploadSaltsSnapshot(RotatingSaltProvider.SaltSnapshot snapshot, Str } } - cloudStorage.upload(newSaltsFile.toString(), location, this.currentTags); + //cloudStorage.upload(newSaltsFile.toString(), location, this.currentTags); + this.upload(newSaltsFile.toString(), location); + } + + protected void upload(String data, String location) throws Exception { + cloudStorage.upload(data, location, this.currentTags); + } private void setStatusTagToCurrent(String location) throws CloudStorageException { diff --git a/src/main/java/com/uid2/admin/util/PrivateSiteUtil.java b/src/main/java/com/uid2/admin/util/PrivateSiteUtil.java index f73541a9..dd81e397 100644 --- a/src/main/java/com/uid2/admin/util/PrivateSiteUtil.java +++ b/src/main/java/com/uid2/admin/util/PrivateSiteUtil.java @@ -6,6 +6,7 @@ import com.uid2.shared.auth.*; import com.uid2.shared.model.EncryptionKey; import com.uid2.shared.model.KeysetKey; +import com.uid2.shared.model.SaltEntry; import com.uid2.shared.model.Site; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -310,4 +311,18 @@ public static PrivateSiteDataMap getKeysetKeys(Collection getPrivateSaltEntries( + Collection globalSaltEntries, + Collection operators) { + final PrivateSiteDataMap result = getPrivateSites(operators); + + globalSaltEntries.forEach(saltEntry -> { + result.forEach((publicSiteId, publicSiteData) -> { + publicSiteData.add(saltEntry); + }); + }); + + return result; + } } diff --git a/src/main/java/com/uid2/admin/util/PublicSiteUtil.java b/src/main/java/com/uid2/admin/util/PublicSiteUtil.java index bc55bc14..7470c5c2 100644 --- a/src/main/java/com/uid2/admin/util/PublicSiteUtil.java +++ b/src/main/java/com/uid2/admin/util/PublicSiteUtil.java @@ -8,6 +8,7 @@ import com.uid2.shared.auth.OperatorType; import com.uid2.shared.model.EncryptionKey; import com.uid2.shared.model.KeysetKey; +import com.uid2.shared.model.SaltEntry; import com.uid2.shared.model.Site; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -128,4 +129,18 @@ public static PrivateSiteDataMap getPublicKeysetKeys( return result; } + + public static PrivateSiteDataMap getPublicSaltEntries( + Collection globalSaltEntries, + Collection operators) { + final PrivateSiteDataMap result = getPublicSitesMap(operators); + + globalSaltEntries.forEach(saltEntry -> { + result.forEach((publicSiteId, publicSiteData) -> { + publicSiteData.add(saltEntry); + }); + }); + + return result; + } }