From 3a819c242364b5077ce1397ca25e75fc09fa6703 Mon Sep 17 00:00:00 2001 From: Elizabeth Healy Date: Mon, 19 Aug 2024 23:02:11 -0400 Subject: [PATCH 1/5] passing unit tests --- .../java/io/opentdf/platform/Command.java | 12 ++-- .../opentdf/platform/sdk/Autoconfigure.java | 29 +++++++-- .../java/io/opentdf/platform/sdk/Config.java | 64 ++++++++++++++++++- .../java/io/opentdf/platform/sdk/TDF.java | 54 ++++++++++++++-- .../io/opentdf/platform/sdk/ConfigTest.java | 8 +-- .../io/opentdf/platform/sdk/TDFE2ETest.java | 2 +- .../java/io/opentdf/platform/sdk/TDFTest.java | 29 +++++++-- 7 files changed, 164 insertions(+), 34 deletions(-) diff --git a/cmdline/src/main/java/io/opentdf/platform/Command.java b/cmdline/src/main/java/io/opentdf/platform/Command.java index 64886f65..fb912074 100644 --- a/cmdline/src/main/java/io/opentdf/platform/Command.java +++ b/cmdline/src/main/java/io/opentdf/platform/Command.java @@ -13,12 +13,10 @@ import javax.crypto.NoSuchPaddingException; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; -import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.PrintWriter; -import java.io.StringWriter; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.nio.file.Path; @@ -31,7 +29,6 @@ import java.util.List; import java.util.Optional; import java.util.function.Consumer; -import java.util.stream.Stream; @CommandLine.Command(name = "tdf") class Command { @@ -69,14 +66,13 @@ void encrypt( configs.add(Config.withKasInformation(kasInfos)); metadata.map(Config::withMetaData).ifPresent(configs::add); mimeType.map(Config::withMimeType).ifPresent(configs::add); - attributes.ifPresent(attr -> { - configs.add(Config.withDataAttributes(attr.split(","))); - }); - + if (attributes.isPresent()){ + configs.add(Config.withDataAttributes(attributes.get().split(","))); + } var tdfConfig = Config.newTDFConfig(configs.toArray(Consumer[]::new)); try (var in = file.isEmpty() ? new BufferedInputStream(System.in) : new FileInputStream(file.get())) { try (var out = new BufferedOutputStream(System.out)) { - new TDF().createTDF(in, out, tdfConfig, sdk.getServices().kas()); + new TDF().createTDF(in, out, tdfConfig, sdk.getServices().kas(), sdk.getServices().attributes()); } } } diff --git a/sdk/src/main/java/io/opentdf/platform/sdk/Autoconfigure.java b/sdk/src/main/java/io/opentdf/platform/sdk/Autoconfigure.java index 429d36a4..9cf20edb 100644 --- a/sdk/src/main/java/io/opentdf/platform/sdk/Autoconfigure.java +++ b/sdk/src/main/java/io/opentdf/platform/sdk/Autoconfigure.java @@ -1,5 +1,6 @@ package io.opentdf.platform.sdk; +import java.io.IOException; import java.net.URLDecoder; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; @@ -31,7 +32,7 @@ import io.opentdf.platform.policy.AttributeRuleTypeEnum; // Error handling class -class AutoConfigureException extends Exception { +class AutoConfigureException extends IOException { public AutoConfigureException(String message) { super(message); } @@ -165,6 +166,20 @@ public AttributeValueFQN(String url) throws AutoConfigureException { public String toString() { return url; } + + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null || !(obj instanceof AttributeValueFQN)) { + return false; + } + AttributeValueFQN afqn = (AttributeValueFQN) obj; + if ((this.url.equals(afqn.url)) && (this.key.equals(afqn.key))){ + return true; + } + return false; + } public String getKey() { return key; @@ -273,7 +288,7 @@ public interface StringOperator { } - public List plan(List defaultKas, Supplier genSplitID) throws Exception { + public List plan(List defaultKas, Supplier genSplitID) throws AutoConfigureException { AttributeBooleanExpression b = constructAttributeBoolean(); BooleanKeyExpression k = insertKeysForAttribute(b); if (k == null) { @@ -307,7 +322,7 @@ public List plan(List defaultKas, Supplier genSplitID return steps; } - public BooleanKeyExpression insertKeysForAttribute(AttributeBooleanExpression e) throws Exception { + public BooleanKeyExpression insertKeysForAttribute(AttributeBooleanExpression e) throws AutoConfigureException { List kcs = new ArrayList<>(e.must.size()); for (SingleAttributeClause clause : e.must) { @@ -316,7 +331,7 @@ public BooleanKeyExpression insertKeysForAttribute(AttributeBooleanExpression e) for (AttributeValueFQN term : clause.values) { KeyAccessGrant grant = byAttribute(term); if (grant == null) { - throw new Exception(String.format("no definition or grant found for [%s]", term)); + throw new AutoConfigureException(String.format("no definition or grant found for [%s]", term)); } List kases = grant.kases; @@ -660,7 +675,7 @@ public static String ruleToOperator(AttributeRuleTypeEnum e) { } // Gets a list of directory of KAS grants for a list of attribute FQNs - public static Granter newGranterFromService(SDK.AttributesService as, AttributeValueFQN... fqns) throws Exception { + public static Granter newGranterFromService(SDK.AttributesService as, AttributeValueFQN... fqns) throws AutoConfigureException { String[] fqnsStr = new String[fqns.length]; for (int i = 0; i < fqns.length; i++) { fqnsStr[i] = fqns[i].toString(); @@ -704,7 +719,7 @@ public static Granter newGranterFromService(SDK.AttributesService as, AttributeV // Given a policy (list of data attributes or tags), // get a set of grants from attribute values to KASes. // Unlike `NewGranterFromService`, this works offline. - public static Granter newGranterFromAttributes(Value... attrs) throws Exception { + public static Granter newGranterFromAttributes(Value... attrs) throws AutoConfigureException { List policyList = new ArrayList<>(attrs.length); Map grantsMap = new HashMap<>(); @@ -721,7 +736,7 @@ public static Granter newGranterFromAttributes(Value... attrs) throws Exception grants.policy.add(fqn); Attribute def = v.getAttribute(); if (def == null) { - throw new Exception("No associated definition with value [" + fqn.toString() + "]"); + throw new AutoConfigureException("No associated definition with value [" + fqn.toString() + "]"); } grants.addAllGrants(fqn, def.getGrantsList(), def); diff --git a/sdk/src/main/java/io/opentdf/platform/sdk/Config.java b/sdk/src/main/java/io/opentdf/platform/sdk/Config.java index 108bfffe..a7a081c9 100644 --- a/sdk/src/main/java/io/opentdf/platform/sdk/Config.java +++ b/sdk/src/main/java/io/opentdf/platform/sdk/Config.java @@ -1,9 +1,12 @@ package io.opentdf.platform.sdk; +import io.opentdf.platform.sdk.Autoconfigure.AttributeValueFQN; import io.opentdf.platform.sdk.nanotdf.ECCMode; import io.opentdf.platform.sdk.nanotdf.NanoTDFType; import io.opentdf.platform.sdk.nanotdf.SymmetricAndPayloadConfig; +import io.opentdf.platform.policy.Value; + import java.util.*; import java.util.function.Consumer; @@ -30,6 +33,7 @@ public static class KASInfo { public String URL; public String PublicKey; public String KID; + public Boolean Default; } @@ -70,6 +74,7 @@ public static Consumer withAssertionVerificationKeys(AssertionV } public static class TDFConfig { + public Boolean autoconfigure; public int defaultSegmentSize; public boolean enableEncryption; public TDFFormat tdfFormat; @@ -78,13 +83,15 @@ public static class TDFConfig { public String metaData; public IntegrityAlgorithm integrityAlgorithm; public IntegrityAlgorithm segmentIntegrityAlgorithm; - public List attributes; + public List attributes; + public List attributeValues; public List kasInfoList; public List assertionConfigList; public String mimeType; public List splitPlan; public TDFConfig() { + this.autoconfigure = true; this.defaultSegmentSize = DEFAULT_SEGMENT_SIZE; this.enableEncryption = true; this.tdfFormat = TDFFormat.JSONFormat; @@ -107,9 +114,46 @@ public static TDFConfig newTDFConfig(Consumer... options) { return config; } - public static Consumer withDataAttributes(String... attributes) { + public static Consumer withDataAttributes(String... attributes) throws AutoConfigureException { + List attrValFqns = new ArrayList(); + for (String a : attributes){ + Autoconfigure.AttributeValueFQN attrValFqn = new Autoconfigure.AttributeValueFQN(a); + attrValFqns.add(attrValFqn); + } return (TDFConfig config) -> { - Collections.addAll(config.attributes, attributes); + config.attributeValues = null; + config.attributes.addAll(attrValFqns); + }; + } + + public static Consumer withDataAttributeValues(String... attributes) throws AutoConfigureException { + List attrValFqns = new ArrayList(); + for (String a : attributes){ + Autoconfigure.AttributeValueFQN attrValFqn = new Autoconfigure.AttributeValueFQN(a); + attrValFqns.add(attrValFqn); + } + return (TDFConfig config) -> { + config.attributeValues = null; + config.attributes.addAll(attrValFqns); + }; + } + + // WithDataAttributeValues appends the given data attributes to the bound policy. + // Unlike `WithDataAttributes`, this will not trigger an attribute definition lookup + // during autoconfigure. That is, to use autoconfigure in an 'offline' context, + // you must first store the relevant attribute information locally and load + // it to the `CreateTDF` method with this option. + public static Consumer withDataAttributeValues(Value... attributes) throws AutoConfigureException { + List attrValFqns = new ArrayList(); + List attrVals = new ArrayList(); + for (Value a : attributes) { + attrVals.add(a); + AttributeValueFQN afqn = new Autoconfigure.AttributeValueFQN(a.getFqn()); + attrValFqns.add(afqn); + } + return (TDFConfig config) -> { + config.attributes.addAll(attrValFqns); + config.attributeValues.addAll(attrVals); }; } @@ -119,6 +163,13 @@ public static Consumer withKasInformation(KASInfo... kasInfoList) { }; } + public static Consumer withSplitPlan(Autoconfigure.SplitStep... p) { + return (TDFConfig config) -> { + config.splitPlan = new ArrayList<>(Arrays.asList(p)); + config.autoconfigure = false; + }; + } + public static Consumer withAssertionConfig(io.opentdf.platform.sdk.AssertionConfig... assertionList) { return (TDFConfig config) -> { Collections.addAll(config.assertionConfigList, assertionList); @@ -133,6 +184,13 @@ public static Consumer withSegmentSize(int size) { return (TDFConfig config) -> config.defaultSegmentSize = size; } + public static Consumer withAutoconfigure(boolean enable) { + return (TDFConfig config) -> { + config.autoconfigure = enable; + config.splitPlan = null; + }; + } + // public static Consumer withDisableEncryption() { // return (TDFConfig config) -> config.enableEncryption = false; // } diff --git a/sdk/src/main/java/io/opentdf/platform/sdk/TDF.java b/sdk/src/main/java/io/opentdf/platform/sdk/TDF.java index c8638cb4..4a770dfa 100644 --- a/sdk/src/main/java/io/opentdf/platform/sdk/TDF.java +++ b/sdk/src/main/java/io/opentdf/platform/sdk/TDF.java @@ -8,6 +8,12 @@ import com.nimbusds.jwt.JWTClaimsSet; import com.nimbusds.jwt.SignedJWT; +import io.opentdf.platform.policy.Value; + +import io.opentdf.platform.sdk.Config.TDFConfig; +import io.opentdf.platform.sdk.Autoconfigure.AttributeValueFQN; +import io.opentdf.platform.sdk.Config.KASInfo; + import com.nimbusds.jose.crypto.RSASSASigner; import com.nimbusds.jose.crypto.MACSigner; import org.apache.commons.codec.DecoderException; @@ -148,16 +154,16 @@ public TDFObject() { this.size = 0; } - PolicyObject createPolicyObject(List attributes) { + PolicyObject createPolicyObject(List attributes) { PolicyObject policyObject = new PolicyObject(); policyObject.body = new PolicyObject.Body(); policyObject.uuid = UUID.randomUUID().toString(); policyObject.body.dataAttributes = new ArrayList<>(); policyObject.body.dissem = new ArrayList<>(); - for (String attribute: attributes) { + for (Autoconfigure.AttributeValueFQN attribute: attributes) { PolicyObject.AttributeObject attributeObject = new PolicyObject.AttributeObject(); - attributeObject.attribute = attribute; + attributeObject.attribute = attribute.toString(); policyObject.body.dataAttributes.add(attributeObject); } return policyObject; @@ -172,7 +178,7 @@ private void prepareManifest(Config.TDFConfig tdfConfig, SDK.KAS kas) { String base64PolicyObject = encoder.encodeToString(gson.toJson(policyObject).getBytes(StandardCharsets.UTF_8)); List symKeys = new ArrayList<>(); Map latestKASInfo = new HashMap<>(); - if (tdfConfig.splitPlan.isEmpty()) { + if (tdfConfig.splitPlan == null || tdfConfig.splitPlan.isEmpty()) { // Default split plan: Split keys across all KASes List splitPlan = new ArrayList<>(tdfConfig.kasInfoList.size()); int i = 0; @@ -369,7 +375,28 @@ private static String calculateSignature(byte[] data, byte[] secret, Config.Inte public TDFObject createTDF(InputStream payload, OutputStream outputStream, - Config.TDFConfig tdfConfig, SDK.KAS kas) throws IOException, JOSEException { + Config.TDFConfig tdfConfig, SDK.KAS kas, SDK.AttributesService attrService) throws IOException, JOSEException { + + if (tdfConfig.autoconfigure) { + Autoconfigure.Granter granter; + if (!tdfConfig.attributeValues.isEmpty()) { + granter = Autoconfigure.newGranterFromAttributes(tdfConfig.attributeValues.toArray(new Value[0])); + } else { + granter = Autoconfigure.newGranterFromService(attrService, tdfConfig.attributes.toArray(new AttributeValueFQN[0])); + } + + if (granter == null) { + throw new AutoConfigureException("Failed to create Granter"); // Replace with appropriate error handling + } + + List dk = defaultKas(tdfConfig); + tdfConfig.splitPlan = granter.plan(dk, () -> UUID.randomUUID().toString()); + + if (tdfConfig.splitPlan == null) { + throw new AutoConfigureException("Failed to generate Split Plan"); // Replace with appropriate error handling + } + } + if (tdfConfig.kasInfoList.isEmpty()) { throw new KasInfoMissing("kas information is missing"); } @@ -495,6 +522,23 @@ public TDFObject createTDF(InputStream payload, return tdfObject; } + public List defaultKas(TDFConfig config) { + List allk = new ArrayList<>(); + List defk = new ArrayList<>(); + + for (KASInfo kasInfo : config.kasInfoList) { + if (kasInfo.Default) { + defk.add(kasInfo.URL); + } else if (defk.isEmpty()) { + allk.add(kasInfo.URL); + } + } + if (defk.isEmpty()) { + return allk; + } + return defk; + } + private void fillInPublicKeyInfo(List kasInfoList, SDK.KAS kas) { for (var kasInfo: kasInfoList) { if (kasInfo.PublicKey != null && !kasInfo.PublicKey.isBlank()) { diff --git a/sdk/src/test/java/io/opentdf/platform/sdk/ConfigTest.java b/sdk/src/test/java/io/opentdf/platform/sdk/ConfigTest.java index 0f079f81..cae23434 100644 --- a/sdk/src/test/java/io/opentdf/platform/sdk/ConfigTest.java +++ b/sdk/src/test/java/io/opentdf/platform/sdk/ConfigTest.java @@ -20,11 +20,11 @@ void newTDFConfig_shouldCreateDefaultConfig() { } @Test - void withDataAttributes_shouldAddAttributes() { - Config.TDFConfig config = Config.newTDFConfig(Config.withDataAttributes("attr1", "attr2")); + void withDataAttributes_shouldAddAttributes() throws AutoConfigureException { + Config.TDFConfig config = Config.newTDFConfig(Config.withDataAttributes("https://example.com/attr/attr1/value/value1", "https://example.com/attr/attr2/value/value2")); assertEquals(2, config.attributes.size()); - assertTrue(config.attributes.contains("attr1")); - assertTrue(config.attributes.contains("attr2")); + assertTrue(config.attributes.contains(new Autoconfigure.AttributeValueFQN("https://example.com/attr/attr1/value/value1"))); + assertTrue(config.attributes.contains(new Autoconfigure.AttributeValueFQN("https://example.com/attr/attr2/value/value2"))); } @Test diff --git a/sdk/src/test/java/io/opentdf/platform/sdk/TDFE2ETest.java b/sdk/src/test/java/io/opentdf/platform/sdk/TDFE2ETest.java index eea443c7..fb44663c 100644 --- a/sdk/src/test/java/io/opentdf/platform/sdk/TDFE2ETest.java +++ b/sdk/src/test/java/io/opentdf/platform/sdk/TDFE2ETest.java @@ -35,7 +35,7 @@ public void createAndDecryptTdfIT() throws Exception { ByteArrayOutputStream tdfOutputStream = new ByteArrayOutputStream(); TDF tdf = new TDF(); - tdf.createTDF(plainTextInputStream, tdfOutputStream, config, sdk.kas()); + tdf.createTDF(plainTextInputStream, tdfOutputStream, config, sdk.kas(), sdk.attributes()); var unwrappedData = new java.io.ByteArrayOutputStream(); var reader = tdf.loadTDF(new SeekableInMemoryByteChannel(tdfOutputStream.toByteArray()), sdk.kas()); diff --git a/sdk/src/test/java/io/opentdf/platform/sdk/TDFTest.java b/sdk/src/test/java/io/opentdf/platform/sdk/TDFTest.java index 8fad4ca1..ab773673 100644 --- a/sdk/src/test/java/io/opentdf/platform/sdk/TDFTest.java +++ b/sdk/src/test/java/io/opentdf/platform/sdk/TDFTest.java @@ -7,6 +7,8 @@ import com.nimbusds.jose.crypto.RSASSAVerifier; import com.nimbusds.jose.jwk.RSAKey; +import io.opentdf.platform.policy.attributes.GetAttributeValuesByFqnsRequest; +import io.opentdf.platform.policy.attributes.GetAttributeValuesByFqnsResponse; import io.opentdf.platform.sdk.nanotdf.NanoTDFType; import org.apache.commons.compress.utils.SeekableInMemoryByteChannel; import org.junit.jupiter.api.BeforeAll; @@ -69,6 +71,15 @@ public byte[] unwrapNanoTDF(NanoTDFType.ECCurve curve, String header, String kas return null; } }; + private static SDK.AttributesService attrServ = new SDK.AttributesService() { + @Override + public void close() {} + + @Override + public GetAttributeValuesByFqnsResponse getAttributeValuesByFqn(GetAttributeValuesByFqnsRequest request) { + return GetAttributeValuesByFqnsResponse.newBuilder().build(); + } + }; private static ArrayList keypairs = new ArrayList<>(); @@ -97,6 +108,7 @@ void testSimpleTDFEncryptAndDecrypt() throws Exception { assertion1.assertionKey = new AssertionConfig.AssertionKey(AssertionConfig.AssertionKeyAlg.HS256, key); Config.TDFConfig config = Config.newTDFConfig( + Config.withAutoconfigure(false), Config.withKasInformation(getKASInfos()), Config.withMetaData("here is some metadata"), Config.withAssertionConfig(assertion1) @@ -107,7 +119,7 @@ void testSimpleTDFEncryptAndDecrypt() throws Exception { ByteArrayOutputStream tdfOutputStream = new ByteArrayOutputStream(); TDF tdf = new TDF(); - tdf.createTDF(plainTextInputStream, tdfOutputStream, config, kas); + tdf.createTDF(plainTextInputStream, tdfOutputStream, config, kas, attrServ); var assertionVerificationKeys = new Config.AssertionVerificationKeys(); assertionVerificationKeys.defaultKey = new AssertionConfig.AssertionKey(AssertionConfig.AssertionKeyAlg.HS256, key); @@ -142,6 +154,7 @@ void testSimpleTDFWithAssertionWithRS256() throws Exception { keypair.getPrivate()); Config.TDFConfig config = Config.newTDFConfig( + Config.withAutoconfigure(false), Config.withKasInformation(getKASInfos()), Config.withAssertionConfig(assertionConfig) ); @@ -151,7 +164,7 @@ void testSimpleTDFWithAssertionWithRS256() throws Exception { ByteArrayOutputStream tdfOutputStream = new ByteArrayOutputStream(); TDF tdf = new TDF(); - tdf.createTDF(plainTextInputStream, tdfOutputStream, config, kas); + tdf.createTDF(plainTextInputStream, tdfOutputStream, config, kas, attrServ); var assertionVerificationKeys = new Config.AssertionVerificationKeys(); assertionVerificationKeys.keys.put(assertion1Id, @@ -181,6 +194,7 @@ void testSimpleTDFWithAssertionWithHS256() throws Exception { assertionConfig1.statement.value = "ICAgIDxlZGoOkVkaD4="; Config.TDFConfig config = Config.newTDFConfig( + Config.withAutoconfigure(false), Config.withKasInformation(getKASInfos()), Config.withAssertionConfig(assertionConfig1) ); @@ -190,7 +204,7 @@ void testSimpleTDFWithAssertionWithHS256() throws Exception { ByteArrayOutputStream tdfOutputStream = new ByteArrayOutputStream(); TDF tdf = new TDF(); - tdf.createTDF(plainTextInputStream, tdfOutputStream, config, kas); + tdf.createTDF(plainTextInputStream, tdfOutputStream, config, kas, attrServ); var unwrappedData = new ByteArrayOutputStream(); var reader = tdf.loadTDF(new SeekableInMemoryByteChannel(tdfOutputStream.toByteArray()), kas); @@ -206,6 +220,7 @@ public void testCreatingTDFWithMultipleSegments() throws Exception { var random = new Random(); Config.TDFConfig config = Config.newTDFConfig( + Config.withAutoconfigure(false), Config.withKasInformation(getKASInfos()), // use a random segment size that makes sure that we will use multiple segments Config.withSegmentSize(1 + random.nextInt(20)) @@ -217,7 +232,7 @@ public void testCreatingTDFWithMultipleSegments() throws Exception { var plainTextInputStream = new ByteArrayInputStream(data); var tdfOutputStream = new ByteArrayOutputStream(); var tdf = new TDF(); - tdf.createTDF(plainTextInputStream, tdfOutputStream, config, kas); + tdf.createTDF(plainTextInputStream, tdfOutputStream, config, kas, attrServ); var unwrappedData = new ByteArrayOutputStream(); var reader = tdf.loadTDF(new SeekableInMemoryByteChannel(tdfOutputStream.toByteArray()), kas); reader.readPayload(unwrappedData); @@ -262,10 +277,11 @@ public void write(byte[] b, int off, int len) {} var tdf = new TDF(maxSize); var tdfConfig = Config.newTDFConfig( + Config.withAutoconfigure(false), Config.withKasInformation(getKASInfos()), Config.withSegmentSize(1 + random.nextInt(128))); assertThrows(TDF.DataSizeNotSupported.class, - () -> tdf.createTDF(is, os, tdfConfig, kas), + () -> tdf.createTDF(is, os, tdfConfig, kas, attrServ), "didn't throw an exception when we created TDF that was too large"); assertThat(numReturned.get()) .withFailMessage("test returned the wrong number of bytes") @@ -278,6 +294,7 @@ public void testCreateTDFWithMimeType() throws Exception { final String mimeType = "application/pdf"; Config.TDFConfig config = Config.newTDFConfig( + Config.withAutoconfigure(false), Config.withKasInformation(getKASInfos()), Config.withMimeType(mimeType) ); @@ -287,7 +304,7 @@ public void testCreateTDFWithMimeType() throws Exception { ByteArrayOutputStream tdfOutputStream = new ByteArrayOutputStream(); TDF tdf = new TDF(); - tdf.createTDF(plainTextInputStream, tdfOutputStream, config, kas); + tdf.createTDF(plainTextInputStream, tdfOutputStream, config, kas, attrServ); var reader = tdf.loadTDF(new SeekableInMemoryByteChannel(tdfOutputStream.toByteArray()), kas); assertThat(reader.getManifest().payload.mimeType).isEqualTo(mimeType); From 1fe8de75fa96ab3693fb81de42c9b8a344e09a5b Mon Sep 17 00:00:00 2001 From: Elizabeth Healy Date: Tue, 20 Aug 2024 01:33:31 -0400 Subject: [PATCH 2/5] improvements, run ci on pr --- .github/workflows/checks.yaml | 2 -- .../src/main/java/io/opentdf/platform/Command.java | 5 ++++- sdk/src/main/java/io/opentdf/platform/sdk/TDF.java | 12 ++++++------ 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/.github/workflows/checks.yaml b/.github/workflows/checks.yaml index d985651e..4bb2db52 100644 --- a/.github/workflows/checks.yaml +++ b/.github/workflows/checks.yaml @@ -2,8 +2,6 @@ name: "Checks" on: pull_request: - branches: - - main push: branches: - main diff --git a/cmdline/src/main/java/io/opentdf/platform/Command.java b/cmdline/src/main/java/io/opentdf/platform/Command.java index fb912074..444d822f 100644 --- a/cmdline/src/main/java/io/opentdf/platform/Command.java +++ b/cmdline/src/main/java/io/opentdf/platform/Command.java @@ -72,7 +72,10 @@ void encrypt( var tdfConfig = Config.newTDFConfig(configs.toArray(Consumer[]::new)); try (var in = file.isEmpty() ? new BufferedInputStream(System.in) : new FileInputStream(file.get())) { try (var out = new BufferedOutputStream(System.out)) { - new TDF().createTDF(in, out, tdfConfig, sdk.getServices().kas(), sdk.getServices().attributes()); + new TDF().createTDF(in, out, + tdfConfig, + sdk.getServices().kas(), + sdk.getServices().attributes()); } } } diff --git a/sdk/src/main/java/io/opentdf/platform/sdk/TDF.java b/sdk/src/main/java/io/opentdf/platform/sdk/TDF.java index 4a770dfa..afef2130 100644 --- a/sdk/src/main/java/io/opentdf/platform/sdk/TDF.java +++ b/sdk/src/main/java/io/opentdf/platform/sdk/TDF.java @@ -378,10 +378,10 @@ public TDFObject createTDF(InputStream payload, Config.TDFConfig tdfConfig, SDK.KAS kas, SDK.AttributesService attrService) throws IOException, JOSEException { if (tdfConfig.autoconfigure) { - Autoconfigure.Granter granter; + Autoconfigure.Granter granter = new Autoconfigure.Granter(null); if (!tdfConfig.attributeValues.isEmpty()) { granter = Autoconfigure.newGranterFromAttributes(tdfConfig.attributeValues.toArray(new Value[0])); - } else { + } else if (!tdfConfig.attributes.isEmpty()) { granter = Autoconfigure.newGranterFromService(attrService, tdfConfig.attributes.toArray(new AttributeValueFQN[0])); } @@ -389,7 +389,7 @@ public TDFObject createTDF(InputStream payload, throw new AutoConfigureException("Failed to create Granter"); // Replace with appropriate error handling } - List dk = defaultKas(tdfConfig); + List dk = defaultKases(tdfConfig); tdfConfig.splitPlan = granter.plan(dk, () -> UUID.randomUUID().toString()); if (tdfConfig.splitPlan == null) { @@ -397,8 +397,8 @@ public TDFObject createTDF(InputStream payload, } } - if (tdfConfig.kasInfoList.isEmpty()) { - throw new KasInfoMissing("kas information is missing"); + if (tdfConfig.kasInfoList.isEmpty() && (tdfConfig.splitPlan==null || tdfConfig.splitPlan.isEmpty())) { + throw new KasInfoMissing("kas information is missing, no key access template specified or inferred"); } TDFObject tdfObject = new TDFObject(); @@ -522,7 +522,7 @@ public TDFObject createTDF(InputStream payload, return tdfObject; } - public List defaultKas(TDFConfig config) { + public List defaultKases(TDFConfig config) { List allk = new ArrayList<>(); List defk = new ArrayList<>(); From cf350178c3a12b4ee73e1b6d6e13a344d6b24fff Mon Sep 17 00:00:00 2001 From: Elizabeth Healy Date: Tue, 20 Aug 2024 01:51:18 -0400 Subject: [PATCH 3/5] fix null pointer errors --- sdk/src/main/java/io/opentdf/platform/sdk/TDF.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sdk/src/main/java/io/opentdf/platform/sdk/TDF.java b/sdk/src/main/java/io/opentdf/platform/sdk/TDF.java index afef2130..7e877353 100644 --- a/sdk/src/main/java/io/opentdf/platform/sdk/TDF.java +++ b/sdk/src/main/java/io/opentdf/platform/sdk/TDF.java @@ -378,10 +378,10 @@ public TDFObject createTDF(InputStream payload, Config.TDFConfig tdfConfig, SDK.KAS kas, SDK.AttributesService attrService) throws IOException, JOSEException { if (tdfConfig.autoconfigure) { - Autoconfigure.Granter granter = new Autoconfigure.Granter(null); - if (!tdfConfig.attributeValues.isEmpty()) { + Autoconfigure.Granter granter = new Autoconfigure.Granter(new ArrayList<>()); + if (tdfConfig.attributeValues != null && !tdfConfig.attributeValues.isEmpty()) { granter = Autoconfigure.newGranterFromAttributes(tdfConfig.attributeValues.toArray(new Value[0])); - } else if (!tdfConfig.attributes.isEmpty()) { + } else if (tdfConfig.attributes != null && !tdfConfig.attributes.isEmpty()) { granter = Autoconfigure.newGranterFromService(attrService, tdfConfig.attributes.toArray(new AttributeValueFQN[0])); } @@ -527,7 +527,7 @@ public List defaultKases(TDFConfig config) { List defk = new ArrayList<>(); for (KASInfo kasInfo : config.kasInfoList) { - if (kasInfo.Default) { + if (kasInfo.Default != null && kasInfo.Default) { defk.add(kasInfo.URL); } else if (defk.isEmpty()) { allk.add(kasInfo.URL); From c85302687e48efe649300ed287d7edd47604d831 Mon Sep 17 00:00:00 2001 From: Elizabeth Healy Date: Tue, 20 Aug 2024 02:02:35 -0400 Subject: [PATCH 4/5] add autoconfigure option --- .github/workflows/checks.yaml | 2 +- cmdline/src/main/java/io/opentdf/platform/Command.java | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/checks.yaml b/.github/workflows/checks.yaml index 4bb2db52..947833fe 100644 --- a/.github/workflows/checks.yaml +++ b/.github/workflows/checks.yaml @@ -137,7 +137,7 @@ jobs: --client-secret=secret \ --platform-endpoint=localhost:8080 \ -i \ - encrypt --kas-url=localhost:8080 --mime-type=text/plain --attr https://example.com/attr/attr1/value/value1 -f data -m 'here is some metadata' > test.tdf + encrypt --kas-url=localhost:8080 --mime-type=text/plain --attr https://example.com/attr/attr1/value/value1 --autoconfigure=false -f data -m 'here is some metadata' > test.tdf java -jar target/cmdline.jar \ --client-id=opentdf-sdk \ diff --git a/cmdline/src/main/java/io/opentdf/platform/Command.java b/cmdline/src/main/java/io/opentdf/platform/Command.java index 444d822f..ee723844 100644 --- a/cmdline/src/main/java/io/opentdf/platform/Command.java +++ b/cmdline/src/main/java/io/opentdf/platform/Command.java @@ -52,6 +52,7 @@ void encrypt( @Option(names = {"-m", "--metadata"}, defaultValue = Option.NULL_VALUE) Optional metadata, // cant split on optional parameters @Option(names = {"-a", "--attr"}, defaultValue = Option.NULL_VALUE) Optional attributes, + @Option(names = {"-c", "--autoconfigure"}, defaultValue = Option.NULL_VALUE) Optional autoconfigure, @Option(names = {"--mime-type"}, defaultValue = Option.NULL_VALUE) Optional mimeType) throws IOException, JOSEException { @@ -65,6 +66,7 @@ void encrypt( List> configs = new ArrayList<>(); configs.add(Config.withKasInformation(kasInfos)); metadata.map(Config::withMetaData).ifPresent(configs::add); + autoconfigure.map(Config::withAutoconfigure).ifPresent(configs::add); mimeType.map(Config::withMimeType).ifPresent(configs::add); if (attributes.isPresent()){ configs.add(Config.withDataAttributes(attributes.get().split(","))); @@ -72,8 +74,7 @@ void encrypt( var tdfConfig = Config.newTDFConfig(configs.toArray(Consumer[]::new)); try (var in = file.isEmpty() ? new BufferedInputStream(System.in) : new FileInputStream(file.get())) { try (var out = new BufferedOutputStream(System.out)) { - new TDF().createTDF(in, out, - tdfConfig, + new TDF().createTDF(in, out, tdfConfig, sdk.getServices().kas(), sdk.getServices().attributes()); } From c4457d08d8ddc9ab64e45894dac1f6bfe16b61c1 Mon Sep 17 00:00:00 2001 From: Elizabeth Healy Date: Tue, 20 Aug 2024 02:17:02 -0400 Subject: [PATCH 5/5] add split id to the key access obj --- sdk/src/main/java/io/opentdf/platform/sdk/TDF.java | 1 + 1 file changed, 1 insertion(+) diff --git a/sdk/src/main/java/io/opentdf/platform/sdk/TDF.java b/sdk/src/main/java/io/opentdf/platform/sdk/TDF.java index 7e877353..8b8b98fc 100644 --- a/sdk/src/main/java/io/opentdf/platform/sdk/TDF.java +++ b/sdk/src/main/java/io/opentdf/platform/sdk/TDF.java @@ -273,6 +273,7 @@ private void prepareManifest(Config.TDFConfig tdfConfig, SDK.KAS kas) { keyAccess.policyBinding = policyBinding; keyAccess.wrappedKey = encoder.encodeToString(wrappedKey); keyAccess.encryptedMetadata = encryptedMetadata; + keyAccess.sid = splitID; manifest.encryptionInformation.keyAccessObj.add(keyAccess); }