diff --git a/cmdline/src/main/java/io/opentdf/platform/Command.java b/cmdline/src/main/java/io/opentdf/platform/Command.java index 28235d76..408998f1 100644 --- a/cmdline/src/main/java/io/opentdf/platform/Command.java +++ b/cmdline/src/main/java/io/opentdf/platform/Command.java @@ -8,7 +8,6 @@ import io.opentdf.platform.sdk.Config; import io.opentdf.platform.sdk.KeyType; import io.opentdf.platform.sdk.Config.AssertionVerificationKeys; -import io.opentdf.platform.sdk.NanoTDF; import io.opentdf.platform.sdk.SDK; import io.opentdf.platform.sdk.SDKBuilder; import io.opentdf.platform.sdk.TDF; @@ -192,8 +191,8 @@ void encrypt( } catch (JsonSyntaxException e) { // try it as a file path try { - String fielJson = new String(Files.readAllBytes(Paths.get(assertionConfig))); - assertionConfigs = gson.fromJson(fielJson, AssertionConfig[].class); + String fileJson = new String(Files.readAllBytes(Paths.get(assertionConfig))); + assertionConfigs = gson.fromJson(fileJson, AssertionConfig[].class); } catch (JsonSyntaxException e2) { throw new RuntimeException("Failed to parse assertion from file, expects an list of assertions", e2); } catch(Exception e3) { @@ -221,9 +220,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, - sdk.getServices().kas(), - sdk.getServices().attributes()); + sdk.createTDF(in, out, tdfConfig); } } } @@ -249,56 +246,53 @@ void decrypt(@Option(names = { "-f", "--file" }, required = true) Path tdfPath, @Option(names = { "--with-assertion-verification-keys" }, defaultValue = Option.NULL_VALUE) Optional assertionVerification, @Option(names = { "--kas-allowlist" }, defaultValue = Option.NULL_VALUE) Optional kasAllowlistStr, @Option(names = { "--ignore-kas-allowlist" }, defaultValue = Option.NULL_VALUE) Optional ignoreAllowlist) - throws IOException, TDF.FailedToCreateGMAC, JOSEException, ParseException, NoSuchAlgorithmException, DecoderException, InterruptedException, ExecutionException, URISyntaxException { - var sdk = buildSDK(); - var opts = new ArrayList>(); - try (var in = FileChannel.open(tdfPath, StandardOpenOption.READ)) { - try (var stdout = new BufferedOutputStream(System.out)) { - if (assertionVerification.isPresent()) { - var assertionVerificationInput = assertionVerification.get(); - Gson gson = new Gson(); - - AssertionVerificationKeys assertionVerificationKeys; - try { - assertionVerificationKeys = gson.fromJson(assertionVerificationInput, AssertionVerificationKeys.class); - } catch (JsonSyntaxException e) { - // try it as a file path + throws Exception { + try (var sdk = buildSDK()) { + var opts = new ArrayList>(); + try (var in = FileChannel.open(tdfPath, StandardOpenOption.READ)) { + try (var stdout = new BufferedOutputStream(System.out)) { + if (assertionVerification.isPresent()) { + var assertionVerificationInput = assertionVerification.get(); + Gson gson = new Gson(); + + AssertionVerificationKeys assertionVerificationKeys; try { - String fileJson = new String(Files.readAllBytes(Paths.get(assertionVerificationInput))); - assertionVerificationKeys = gson.fromJson(fileJson, AssertionVerificationKeys.class); - } catch (JsonSyntaxException e2) { - throw new RuntimeException("Failed to parse assertion verification keys from file", e2); - } catch(Exception e3) { - throw new RuntimeException("Could not parse assertion verification keys as json string or path to file", e3); + assertionVerificationKeys = gson.fromJson(assertionVerificationInput, AssertionVerificationKeys.class); + } catch (JsonSyntaxException e) { + // try it as a file path + try { + String fileJson = new String(Files.readAllBytes(Paths.get(assertionVerificationInput))); + assertionVerificationKeys = gson.fromJson(fileJson, AssertionVerificationKeys.class); + } catch (JsonSyntaxException e2) { + throw new RuntimeException("Failed to parse assertion verification keys from file", e2); + } catch (Exception e3) { + throw new RuntimeException("Could not parse assertion verification keys as json string or path to file", e3); + } } - } - for (Map.Entry entry : assertionVerificationKeys.keys.entrySet()){ - try { - Object correctedKey = correctKeyType(entry.getValue().alg, entry.getValue().key, true); - entry.setValue(new AssertionConfig.AssertionKey(entry.getValue().alg, correctedKey)); - } catch (Exception e) { - throw new RuntimeException("Error with assertion verification key: " + e.getMessage(), e); + for (Map.Entry entry : assertionVerificationKeys.keys.entrySet()) { + try { + Object correctedKey = correctKeyType(entry.getValue().alg, entry.getValue().key, true); + entry.setValue(new AssertionConfig.AssertionKey(entry.getValue().alg, correctedKey)); + } catch (Exception e) { + throw new RuntimeException("Error with assertion verification key: " + e.getMessage(), e); + } } + opts.add(Config.withAssertionVerificationKeys(assertionVerificationKeys)); } - opts.add(Config.withAssertionVerificationKeys(assertionVerificationKeys)); - } - if (disableAssertionVerification) { - opts.add(Config.withDisableAssertionVerification(true)); - } - rewrapKeyType.map(Config::WithSessionKeyType).ifPresent(opts::add); + if (disableAssertionVerification) { + opts.add(Config.withDisableAssertionVerification(true)); + } + rewrapKeyType.map(Config::WithSessionKeyType).ifPresent(opts::add); - if (ignoreAllowlist.isPresent()) { - opts.add(Config.WithIgnoreKasAllowlist(ignoreAllowlist.get())); - } - if (kasAllowlistStr.isPresent()) { - opts.add(Config.WithKasAllowlist(kasAllowlistStr.get().split(","))); - } + ignoreAllowlist.ifPresent(aBoolean -> opts.add(Config.WithIgnoreKasAllowlist(aBoolean))); + kasAllowlistStr.ifPresent(s -> opts.add(Config.WithKasAllowlist(s.split(",")))); - var readerConfig = Config.newTDFReaderConfig(opts.toArray(new Consumer[0])); - var reader = new TDF().loadTDF(in, sdk.getServices().kas(), readerConfig, sdk.getServices().kasRegistry(), sdk.getPlatformUrl()); - reader.readPayload(stdout); + var readerConfig = Config.newTDFReaderConfig(opts.toArray(new Consumer[0])); + var reader = sdk.loadTDF(in, readerConfig); + reader.readPayload(stdout); + } } } } @@ -313,15 +307,11 @@ void readMetadata(@Option(names = { "-f", "--file" }, required = true) Path tdfP try (var in = FileChannel.open(tdfPath, StandardOpenOption.READ)) { try (var stdout = new PrintWriter(System.out)) { - if (ignoreAllowlist.isPresent()) { - opts.add(Config.WithIgnoreKasAllowlist(ignoreAllowlist.get())); - } - if (kasAllowlistStr.isPresent()) { - opts.add(Config.WithKasAllowlist(kasAllowlistStr.get().split(","))); - } + ignoreAllowlist.map(Config::WithIgnoreKasAllowlist).ifPresent(opts::add); + kasAllowlistStr.map(s -> s.split(",")).map(Config::WithKasAllowlist).ifPresent(opts::add); var readerConfig = Config.newTDFReaderConfig(opts.toArray(new Consumer[0])); - var reader = new TDF().loadTDF(in, sdk.getServices().kas(), readerConfig, sdk.getServices().kasRegistry(), sdk.getPlatformUrl()); + var reader = sdk.loadTDF(in, readerConfig); stdout.write(reader.getMetadata() == null ? "" : reader.getMetadata()); } } @@ -351,8 +341,7 @@ void createNanoTDF( var nanoTDFConfig = Config.newNanoTDFConfig(configs.toArray(Consumer[]::new)); try (var in = file.isEmpty() ? new BufferedInputStream(System.in) : new FileInputStream(file.get())) { try (var out = new BufferedOutputStream(System.out)) { - NanoTDF ntdf = new NanoTDF(); - ntdf.createNanoTDF(ByteBuffer.wrap(in.readAllBytes()), out, nanoTDFConfig, sdk.getServices().kas()); + sdk.createNanoTDF(ByteBuffer.wrap(in.readAllBytes()), out, nanoTDFConfig); } } } @@ -364,19 +353,14 @@ void readNanoTDF(@Option(names = { "-f", "--file" }, required = true) Path nanoT var sdk = buildSDK(); try (var in = FileChannel.open(nanoTDFPath, StandardOpenOption.READ)) { try (var stdout = new BufferedOutputStream(System.out)) { - NanoTDF ntdf = new NanoTDF(); ByteBuffer buffer = ByteBuffer.allocate((int) in.size()); in.read(buffer); buffer.flip(); var opts = new ArrayList>(); - if (ignoreAllowlist.isPresent()) { - opts.add(Config.WithNanoIgnoreKasAllowlist(ignoreAllowlist.get())); - } - if (kasAllowlistStr.isPresent()) { - opts.add(Config.WithNanoKasAllowlist(kasAllowlistStr.get().split(","))); - } + ignoreAllowlist.map(Config::WithNanoIgnoreKasAllowlist).ifPresent(opts::add); + kasAllowlistStr.map(s -> s.split(",")).map(Config::WithNanoKasAllowlist).ifPresent(opts::add); var readerConfig = Config.newNanoTDFReaderConfig(opts.toArray(new Consumer[0])); - ntdf.readNanoTDF(buffer, stdout, sdk.getServices().kas(), readerConfig, sdk.getServices().kasRegistry(), sdk.getPlatformUrl()); + sdk.readNanoTDF(buffer, stdout, readerConfig); } } } diff --git a/examples/src/main/java/io/opentdf/platform/DecryptCollectionExample.java b/examples/src/main/java/io/opentdf/platform/DecryptCollectionExample.java index 7a03c764..d1992caf 100644 --- a/examples/src/main/java/io/opentdf/platform/DecryptCollectionExample.java +++ b/examples/src/main/java/io/opentdf/platform/DecryptCollectionExample.java @@ -31,11 +31,9 @@ public static void main(String[] args) throws IOException, NanoTDF.NanoTDFMaxSiz // Convert String to InputStream - NanoTDF nanoTDFClient = new NanoTDF(true); - for (int i = 0; i < 50; i++) { FileInputStream fis = new FileInputStream(String.format("out/my.%d_ciphertext", i)); - nanoTDFClient.readNanoTDF(ByteBuffer.wrap(fis.readAllBytes()), System.out, sdk.getServices().kas(), sdk.getServices().kasRegistry(), sdk.getPlatformUrl()); + sdk.readNanoTDF(ByteBuffer.wrap(fis.readAllBytes()), System.out, Config.newNanoTDFReaderConfig()); fis.close(); } diff --git a/examples/src/main/java/io/opentdf/platform/DecryptExample.java b/examples/src/main/java/io/opentdf/platform/DecryptExample.java index e2f43f7c..fe608598 100644 --- a/examples/src/main/java/io/opentdf/platform/DecryptExample.java +++ b/examples/src/main/java/io/opentdf/platform/DecryptExample.java @@ -56,7 +56,7 @@ public static void main(String[] args) throws IOException, Path path = Paths.get("my.ciphertext"); try (var in = FileChannel.open(path, StandardOpenOption.READ)) { - var reader = new TDF().loadTDF(in, sdk.getServices().kas(), Config.newTDFReaderConfig(Config.WithSessionKeyType(sessionKeyType)), sdk.getServices().kasRegistry(), sdk.getPlatformUrl()); + var reader = sdk.loadTDF(in, Config.newTDFReaderConfig(Config.WithSessionKeyType(sessionKeyType))); reader.readPayload(System.out); } diff --git a/examples/src/main/java/io/opentdf/platform/EncryptCollectionExample.java b/examples/src/main/java/io/opentdf/platform/EncryptCollectionExample.java index 7f431a2c..585ba6b4 100644 --- a/examples/src/main/java/io/opentdf/platform/EncryptCollectionExample.java +++ b/examples/src/main/java/io/opentdf/platform/EncryptCollectionExample.java @@ -35,15 +35,9 @@ public static void main(String[] args) throws IOException, NanoTDF.NanoTDFMaxSiz String str = "Hello, World!"; - // Convert String to InputStream - var in = new ByteArrayInputStream(str.getBytes(StandardCharsets.UTF_8)); - NanoTDF nanoTDFClient = new NanoTDF(); - for (int i = 0; i < 50; i++) { FileOutputStream fos = new FileOutputStream(String.format("out/my.%d_ciphertext", i)); - nanoTDFClient.createNanoTDF(ByteBuffer.wrap(str.getBytes()), fos, tdfConfig, - sdk.getServices().kas()); + sdk.createNanoTDF(ByteBuffer.wrap(str.getBytes(StandardCharsets.UTF_8)), fos, tdfConfig); } - } } diff --git a/examples/src/main/java/io/opentdf/platform/EncryptExample.java b/examples/src/main/java/io/opentdf/platform/EncryptExample.java index f9ac0176..0231ede4 100644 --- a/examples/src/main/java/io/opentdf/platform/EncryptExample.java +++ b/examples/src/main/java/io/opentdf/platform/EncryptExample.java @@ -53,8 +53,6 @@ public static void main(String[] args) throws IOException, JOSEException, AutoCo FileOutputStream fos = new FileOutputStream("my.ciphertext"); - new TDF().createTDF(in, fos, tdfConfig, - sdk.getServices().kas(), - sdk.getServices().attributes()); + sdk.createTDF(in, fos, tdfConfig); } } \ No newline at end of file diff --git a/sdk/src/main/java/io/opentdf/platform/sdk/AutoConfigureException.java b/sdk/src/main/java/io/opentdf/platform/sdk/AutoConfigureException.java index 157e6a8a..02217452 100644 --- a/sdk/src/main/java/io/opentdf/platform/sdk/AutoConfigureException.java +++ b/sdk/src/main/java/io/opentdf/platform/sdk/AutoConfigureException.java @@ -3,16 +3,11 @@ /** * Exception thrown when automatic configuration fails. */ -public class AutoConfigureException extends RuntimeException { +public class AutoConfigureException extends SDKException { public AutoConfigureException(String message) { super(message); } - - public AutoConfigureException(Exception e) { - super(e); - } - - public AutoConfigureException(String message, Exception e) { - super(message, e); + public AutoConfigureException(String message, Exception cause) { + super(message, cause); } } 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 6113a681..7db8476a 100644 --- a/sdk/src/main/java/io/opentdf/platform/sdk/Autoconfigure.java +++ b/sdk/src/main/java/io/opentdf/platform/sdk/Autoconfigure.java @@ -699,14 +699,22 @@ public static Granter newGranterFromAttributes(Value... attrValues) throws AutoC } // Gets a list of directory of KAS grants for a list of attribute FQNs - public static Granter newGranterFromService(AttributesServiceFutureStub as, KASKeyCache keyCache, AttributeValueFQN... fqns) throws AutoConfigureException, ExecutionException, InterruptedException { + static Granter newGranterFromService(AttributesServiceFutureStub as, KASKeyCache keyCache, AttributeValueFQN... fqns) throws AutoConfigureException { GetAttributeValuesByFqnsRequest request = GetAttributeValuesByFqnsRequest.newBuilder() .addAllFqns(Arrays.stream(fqns).map(AttributeValueFQN::toString).collect(Collectors.toList())) .setWithValue(AttributeValueSelector.newBuilder().setWithKeyAccessGrants(true).build()) .build(); - GetAttributeValuesByFqnsResponse av = as.getAttributeValuesByFqns(request).get(); + GetAttributeValuesByFqnsResponse av = null; + try { + av = as.getAttributeValuesByFqns(request).get(); + } catch (ExecutionException e) { + throw new AutoConfigureException("error getting attributes during autoconfiguration", e); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new AutoConfigureException("interrupted while getting attributes during autoconfiguration", e); + } return getGranter(keyCache, new ArrayList<>(av.getFqnAttributeValuesMap().values())); } 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 bf3b9d2a..090cdaad 100644 --- a/sdk/src/main/java/io/opentdf/platform/sdk/Config.java +++ b/sdk/src/main/java/io/opentdf/platform/sdk/Config.java @@ -130,16 +130,9 @@ public static Consumer WithSessionKeyType(KeyType keyType) { return (TDFReaderConfig config) -> config.sessionKeyType = keyType; } public static Consumer WithKasAllowlist(String... kasAllowlist) { - return (TDFReaderConfig config) -> { - config.kasAllowlist = Arrays.stream(kasAllowlist) - .map(s -> { - try { - return getKasAddress(s); - } catch (URISyntaxException e) { - throw new RuntimeException("Invalid URI: " + s, e); - } - }).collect(Collectors.toCollection(HashSet::new)); - }; + return (TDFReaderConfig config) -> config.kasAllowlist = Arrays + .stream(kasAllowlist) + .map(Config::getKasAddress).collect(Collectors.toSet()); } public static Consumer withKasAllowlist(Set kasAllowlist) { @@ -401,13 +394,8 @@ public static Consumer WithNanoKasAllowlist(String... kasAl return (NanoTDFReaderConfig config) -> { // apply getKasAddress to each kasAllowlist entry and add to hashset config.kasAllowlist = Arrays.stream(kasAllowlist) - .map(s -> { - try { - return getKasAddress(s); - } catch (URISyntaxException e) { - throw new RuntimeException("Invalid URI: " + s, e); - } - }).collect(Collectors.toCollection(HashSet::new)); + .map(Config::getKasAddress) + .collect(Collectors.toSet()); }; } @@ -456,7 +444,7 @@ public CollectionConfig(boolean useCollection) { this.useCollection = useCollection; } - public synchronized HeaderInfo getHeaderInfo() throws InterruptedException { + public synchronized HeaderInfo getHeaderInfo() throws SDKException { int iteration = iterationCounter; iterationCounter = (iterationCounter + 1) % MAX_COLLECTION_ITERATION; @@ -465,7 +453,12 @@ public synchronized HeaderInfo getHeaderInfo() throws InterruptedException { return null; } while (!updatedHeaderInfo) { - this.wait(); + try { + this.wait(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new SDKException("interrupted while waiting for header info", e); + } } return new HeaderInfo(headerInfo.getHeader(), headerInfo.getKey(), iteration); } @@ -477,13 +470,18 @@ public synchronized void updateHeaderInfo(HeaderInfo headerInfo) { } } - public static String getKasAddress(String kasURL) throws URISyntaxException { + public static String getKasAddress(String kasURL) throws SDKException { // Prepend "https://" if no scheme is provided if (!kasURL.contains("://")) { kasURL = "https://" + kasURL; } - URI uri = new URI(kasURL); + URI uri; + try { + uri = new URI(kasURL); + } catch (URISyntaxException e) { + throw new SDKException("error constructing KAS url", e); + } // Default to "https" if no scheme is provided String scheme = uri.getScheme(); @@ -498,6 +496,10 @@ public static String getKasAddress(String kasURL) throws URISyntaxException { } // Reconstruct the URL with only the scheme, host, and port - return new URI(scheme, null, uri.getHost(), port, null, null, null).toString(); + try { + return new URI(scheme, null, uri.getHost(), port, null, null, null).toString(); + } catch (URISyntaxException e) { + throw new SDKException("error creating KAS URL from host and port", e); + } } } diff --git a/sdk/src/main/java/io/opentdf/platform/sdk/NanoTDF.java b/sdk/src/main/java/io/opentdf/platform/sdk/NanoTDF.java index 315ce915..4890d859 100644 --- a/sdk/src/main/java/io/opentdf/platform/sdk/NanoTDF.java +++ b/sdk/src/main/java/io/opentdf/platform/sdk/NanoTDF.java @@ -1,6 +1,5 @@ package io.opentdf.platform.sdk; -import io.opentdf.platform.policy.kasregistry.KeyAccessServerRegistryServiceGrpc.KeyAccessServerRegistryServiceFutureStub; import io.opentdf.platform.policy.kasregistry.ListKeyAccessServersRequest; import io.opentdf.platform.policy.kasregistry.ListKeyAccessServersResponse; import io.opentdf.platform.sdk.TDF.KasAllowlistException; @@ -8,7 +7,6 @@ import java.io.IOException; import java.io.OutputStream; -import java.net.URISyntaxException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.charset.StandardCharsets; @@ -38,40 +36,41 @@ public class NanoTDF { private static final int kIvPadding = 9; private static final int kNanoTDFIvSize = 3; private static final byte[] kEmptyIV = new byte[] { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }; + private final SDK.Services services; private final CollectionStore collectionStore; - public NanoTDF() { - this(new CollectionStore.NoOpCollectionStore()); + NanoTDF(SDK.Services services) { + this(services, new CollectionStore.NoOpCollectionStore()); } - public NanoTDF(boolean collectionStoreEnabled) { - this(collectionStoreEnabled ? new CollectionStoreImpl() : null); + NanoTDF(SDK.Services services, boolean collectionStoreEnabled) { + this(services, collectionStoreEnabled ? new CollectionStoreImpl() : null); } - public NanoTDF(CollectionStore collectionStore) { + NanoTDF(SDK.Services services, CollectionStore collectionStore) { + this.services = services; this.collectionStore = collectionStore; } - public static class NanoTDFMaxSizeLimit extends Exception { + public static class NanoTDFMaxSizeLimit extends SDKException { public NanoTDFMaxSizeLimit(String errorMessage) { super(errorMessage); } } - public static class UnsupportedNanoTDFFeature extends Exception { + public static class UnsupportedNanoTDFFeature extends SDKException { public UnsupportedNanoTDFFeature(String errorMessage) { super(errorMessage); } } - public static class InvalidNanoTDFConfig extends Exception { + public static class InvalidNanoTDFConfig extends SDKException { public InvalidNanoTDFConfig(String errorMessage) { super(errorMessage); } } - private Config.HeaderInfo getHeaderInfo(Config.NanoTDFConfig nanoTDFConfig, SDK.KAS kas) - throws InvalidNanoTDFConfig, UnsupportedNanoTDFFeature, NoSuchAlgorithmException, InterruptedException { + private Config.HeaderInfo getHeaderInfo(Config.NanoTDFConfig nanoTDFConfig) throws InvalidNanoTDFConfig, UnsupportedNanoTDFFeature { if (nanoTDFConfig.collectionConfig.useCollection) { Config.HeaderInfo headerInfo = nanoTDFConfig.collectionConfig.getHeaderInfo(); if (headerInfo != null) { @@ -88,7 +87,7 @@ private Config.HeaderInfo getHeaderInfo(Config.NanoTDFConfig nanoTDFConfig, SDK. String url = kasInfo.URL; if (kasInfo.PublicKey == null || kasInfo.PublicKey.isEmpty()) { logger.info("no public key provided for KAS at {}, retrieving", url); - kasInfo = kas.getECPublicKey(kasInfo, nanoTDFConfig.eccMode.getEllipticCurveType()); + kasInfo = services.kas().getECPublicKey(kasInfo, nanoTDFConfig.eccMode.getEllipticCurveType()); } // Kas url resource locator @@ -102,7 +101,12 @@ private Config.HeaderInfo getHeaderInfo(Config.NanoTDFConfig nanoTDFConfig, SDK. byte[] symmetricKey = ECKeyPair.computeECDHKey(kasPublicKey, keyPair.getPrivateKey()); // Generate HKDF key - MessageDigest digest = MessageDigest.getInstance("SHA-256"); + MessageDigest digest = null; + try { + digest = MessageDigest.getInstance("SHA-256"); + } catch (NoSuchAlgorithmException e) { + throw new SDKException("error getting instance of SHA-256 digest", e); + } byte[] hashOfSalt = digest.digest(MAGIC_NUMBER_AND_VERSION); byte[] key = ECKeyPair.calculateHKDF(hashOfSalt, symmetricKey); @@ -148,9 +152,7 @@ private Config.HeaderInfo getHeaderInfo(Config.NanoTDFConfig nanoTDFConfig, SDK. } public int createNanoTDF(ByteBuffer data, OutputStream outputStream, - Config.NanoTDFConfig nanoTDFConfig, - SDK.KAS kas) throws IOException, NanoTDFMaxSizeLimit, InvalidNanoTDFConfig, - NoSuchAlgorithmException, UnsupportedNanoTDFFeature, InterruptedException { + Config.NanoTDFConfig nanoTDFConfig) throws SDKException, IOException { int nanoTDFSize = 0; int dataSize = data.limit(); @@ -158,7 +160,7 @@ public int createNanoTDF(ByteBuffer data, OutputStream outputStream, throw new NanoTDFMaxSizeLimit("exceeds max size for nano tdf"); } - Config.HeaderInfo headerKeyPair = getHeaderInfo(nanoTDFConfig, kas); + Config.HeaderInfo headerKeyPair = getHeaderInfo(nanoTDFConfig); Header header = headerKeyPair.getHeader(); AesGcm gcm = headerKeyPair.getKey(); int iteration = headerKeyPair.getIteration(); @@ -183,7 +185,11 @@ public int createNanoTDF(ByteBuffer data, OutputStream outputStream, } else { do { byte[] iv = new byte[kNanoTDFIvSize]; - SecureRandom.getInstanceStrong().nextBytes(iv); + try { + SecureRandom.getInstanceStrong().nextBytes(iv); + } catch (NoSuchAlgorithmException e) { + throw new SDKException("error getting instance of strong SecureRandom", e); + } System.arraycopy(iv, 0, actualIV, kIvPadding, iv.length); } while (Arrays.equals(actualIV, kEmptyIV)); // if match, we need to retry to prevent key + iv reuse with the policy } @@ -205,22 +211,28 @@ public int createNanoTDF(ByteBuffer data, OutputStream outputStream, return nanoTDFSize; } - public void readNanoTDF(ByteBuffer nanoTDF, OutputStream outputStream, - SDK.KAS kas) throws IOException, URISyntaxException { - readNanoTDF(nanoTDF, outputStream,kas, Config.newNanoTDFReaderConfig()); + public void readNanoTDF(ByteBuffer nanoTDF, OutputStream outputStream) throws IOException { + readNanoTDF(nanoTDF, outputStream, Config.newNanoTDFReaderConfig()); } - public void readNanoTDF(ByteBuffer nanoTDF, OutputStream outputStream, - SDK.KAS kas, KeyAccessServerRegistryServiceFutureStub kasRegistryService, String platformUrl) throws IOException, InterruptedException, ExecutionException, URISyntaxException { - readNanoTDF(nanoTDF, outputStream,kas, Config.newNanoTDFReaderConfig(), kasRegistryService, platformUrl); + public void readNanoTDF(ByteBuffer nanoTDF, OutputStream outputStream, String platformUrl) throws IOException { + readNanoTDF(nanoTDF, outputStream, Config.newNanoTDFReaderConfig(), platformUrl); } public void readNanoTDF(ByteBuffer nanoTDF, OutputStream outputStream, - SDK.KAS kas, Config.NanoTDFReaderConfig nanoTdfReaderConfig, KeyAccessServerRegistryServiceFutureStub kasRegistryService, String platformUrl) throws IOException, InterruptedException, ExecutionException, URISyntaxException { + Config.NanoTDFReaderConfig nanoTdfReaderConfig, String platformUrl) throws IOException, SDKException { if (!nanoTdfReaderConfig.ignoreKasAllowlist && (nanoTdfReaderConfig.kasAllowlist == null || nanoTdfReaderConfig.kasAllowlist.isEmpty())) { ListKeyAccessServersRequest request = ListKeyAccessServersRequest.newBuilder() .build(); - ListKeyAccessServersResponse response = kasRegistryService.listKeyAccessServers(request).get(); + ListKeyAccessServersResponse response = null; + try { + response = services.kasRegistry().listKeyAccessServers(request).get(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new SDKException("interrupted while getting kas registry", e); + } catch (ExecutionException e) { + throw new SDKException("error getting kas registry", e); + } nanoTdfReaderConfig.kasAllowlist = new HashSet<>(); var kases = response.getKeyAccessServersList(); @@ -230,12 +242,12 @@ public void readNanoTDF(ByteBuffer nanoTDF, OutputStream outputStream, nanoTdfReaderConfig.kasAllowlist.add(Config.getKasAddress(platformUrl)); } - readNanoTDF(nanoTDF, outputStream, kas, nanoTdfReaderConfig); + readNanoTDF(nanoTDF, outputStream, nanoTdfReaderConfig); } public void readNanoTDF(ByteBuffer nanoTDF, OutputStream outputStream, - SDK.KAS kas, Config.NanoTDFReaderConfig nanoTdfReaderConfig) throws IOException, URISyntaxException { + Config.NanoTDFReaderConfig nanoTdfReaderConfig) throws IOException { Header header = new Header(nanoTDF); CollectionKey cachedKey = collectionStore.getKey(header); @@ -264,7 +276,7 @@ public void readNanoTDF(ByteBuffer nanoTDF, OutputStream outputStream, } - key = kas.unwrapNanoTDF(header.getECCMode().getEllipticCurveType(), + key = services.kas().unwrapNanoTDF(header.getECCMode().getEllipticCurveType(), base64HeaderData, kasUrl); collectionStore.store(header, new CollectionKey(key)); diff --git a/sdk/src/main/java/io/opentdf/platform/sdk/SDK.java b/sdk/src/main/java/io/opentdf/platform/sdk/SDK.java index d23d106f..541eb200 100644 --- a/sdk/src/main/java/io/opentdf/platform/sdk/SDK.java +++ b/sdk/src/main/java/io/opentdf/platform/sdk/SDK.java @@ -1,11 +1,7 @@ package io.opentdf.platform.sdk; -import io.grpc.CallOptions; -import io.grpc.Channel; -import io.grpc.ClientCall; import io.grpc.ClientInterceptor; import io.grpc.ManagedChannel; -import io.grpc.MethodDescriptor; import io.opentdf.platform.authorization.AuthorizationServiceGrpc; import io.opentdf.platform.authorization.AuthorizationServiceGrpc.AuthorizationServiceFutureStub; import io.opentdf.platform.policy.attributes.AttributesServiceGrpc; @@ -19,11 +15,12 @@ import io.opentdf.platform.policy.kasregistry.KeyAccessServerRegistryServiceGrpc; import io.opentdf.platform.policy.kasregistry.KeyAccessServerRegistryServiceGrpc.KeyAccessServerRegistryServiceFutureStub; import io.opentdf.platform.sdk.nanotdf.NanoTDFType; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import javax.net.ssl.TrustManager; import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.ByteBuffer; import java.nio.channels.SeekableByteChannel; import java.util.Optional; @@ -39,8 +36,6 @@ public class SDK implements AutoCloseable { private final ClientInterceptor authInterceptor; private final String platformUrl; - private static final Logger log = LoggerFactory.getLogger(SDK.class); - /** * Closes the SDK, including its associated services. * @@ -159,6 +154,26 @@ public Services getServices() { return this.services; } + public TDF.Reader loadTDF(SeekableByteChannel channel, Config.TDFReaderConfig config) throws SDKException, IOException { + var tdf = new TDF(services); + return tdf.loadTDF(channel, config, platformUrl); + } + + public TDF.TDFObject createTDF(InputStream payload, OutputStream outputStream, Config.TDFConfig config) throws SDKException, IOException { + var tdf = new TDF(services); + return tdf.createTDF(payload, outputStream, config); + } + + public int createNanoTDF(ByteBuffer payload, OutputStream outputStream, Config.NanoTDFConfig config) throws SDKException, IOException { + var ntdf = new NanoTDF(services); + return ntdf.createNanoTDF(payload, outputStream, config); + } + + public void readNanoTDF(ByteBuffer nanoTDF, OutputStream out, Config.NanoTDFReaderConfig config) throws SDKException, IOException { + var ntdf = new NanoTDF(services); + ntdf.readNanoTDF(nanoTDF, out, config, platformUrl); + } + /** * Checks to see if this has the structure of a Z-TDF in that it is a zip file * containing 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 598522eb..47d03e48 100644 --- a/sdk/src/main/java/io/opentdf/platform/sdk/TDF.java +++ b/sdk/src/main/java/io/opentdf/platform/sdk/TDF.java @@ -5,8 +5,6 @@ import com.nimbusds.jose.*; import io.opentdf.platform.policy.Value; -import io.opentdf.platform.policy.attributes.AttributesServiceGrpc.AttributesServiceFutureStub; -import io.opentdf.platform.policy.kasregistry.KeyAccessServerRegistryServiceGrpc.KeyAccessServerRegistryServiceFutureStub; import io.opentdf.platform.policy.kasregistry.ListKeyAccessServersRequest; import io.opentdf.platform.policy.kasregistry.ListKeyAccessServersResponse; import io.opentdf.platform.sdk.Config.TDFConfig; @@ -25,7 +23,6 @@ import java.io.InputStream; import java.io.OutputStream; import java.io.StringReader; -import java.net.URISyntaxException; import java.nio.channels.SeekableByteChannel; import java.nio.charset.StandardCharsets; import java.security.*; @@ -59,6 +56,8 @@ private static byte[] tdfECKeySaltCompute() { private static final String KEY_ACCESS_SECHMA_VERSION = "1.0"; private final long maximumSize; + private final SDK.Services services; + /** * Constructs a new TDF instance using the default maximum input size defined by MAX_TDF_INPUT_SIZE. *

@@ -66,14 +65,15 @@ private static byte[] tdfECKeySaltCompute() { * input size, which controls the maximum size of the input data that can be processed. * For test purposes, an alternative constructor allows for setting a custom maximum input size. */ - public TDF() { - this(MAX_TDF_INPUT_SIZE); + public TDF(SDK.Services services) { + this(MAX_TDF_INPUT_SIZE, services); } // constructor for tests so that we can set a maximum size that's tractable for // tests - TDF(long maximumInputSize) { + public TDF(long maximumInputSize, SDK.Services services) { this.maximumSize = maximumInputSize; + this.services = services; } public static Logger logger = LoggerFactory.getLogger(TDF.class); @@ -99,49 +99,37 @@ public TDF() { private static final Gson gson = new GsonBuilder().create(); - public class SplitKeyException extends IOException { + public class SplitKeyException extends SDKException { public SplitKeyException(String errorMessage) { super(errorMessage); } } - public static class DataSizeNotSupported extends RuntimeException { + public static class DataSizeNotSupported extends SDKException { public DataSizeNotSupported(String errorMessage) { super(errorMessage); } } - public static class FailedToCreateEncodedTDF extends RuntimeException { - public FailedToCreateEncodedTDF(String errorMessage) { - super(errorMessage); - } - } - - public static class KasInfoMissing extends RuntimeException { + public static class KasInfoMissing extends SDKException { public KasInfoMissing(String errorMessage) { super(errorMessage); } } - public static class KasPublicKeyMissing extends RuntimeException { + public static class KasPublicKeyMissing extends SDKException { public KasPublicKeyMissing(String errorMessage) { super(errorMessage); } } - public static class InputStreamReadFailed extends RuntimeException { - public InputStreamReadFailed(String errorMessage) { - super(errorMessage); - } - } - - public static class FailedToCreateGMAC extends RuntimeException { + public static class FailedToCreateGMAC extends SDKException { public FailedToCreateGMAC(String errorMessage) { super(errorMessage); } } - public static class TDFReadFailed extends RuntimeException { + public static class TDFReadFailed extends SDKException { public TDFReadFailed(String errorMessage) { super(errorMessage); } @@ -483,17 +471,14 @@ private static byte[] calculateSignature(byte[] data, byte[] secret, Config.Inte return Arrays.copyOfRange(data, data.length - kGMACPayloadLength, data.length); } - public TDFObject createTDF(InputStream payload, - OutputStream outputStream, - Config.TDFConfig tdfConfig, SDK.KAS kas, AttributesServiceFutureStub attrService) - throws IOException, JOSEException, AutoConfigureException, InterruptedException, ExecutionException, DecoderException { + TDFObject createTDF(InputStream payload, OutputStream outputStream, Config.TDFConfig tdfConfig) throws SDKException, IOException { if (tdfConfig.autoconfigure) { 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 != null && !tdfConfig.attributes.isEmpty()) { - granter = Autoconfigure.newGranterFromService(attrService, kas.getKeyCache(), + granter = Autoconfigure.newGranterFromService(services.attributes(), services.kas().getKeyCache(), tdfConfig.attributes.toArray(new AttributeValueFQN[0])); } @@ -515,7 +500,7 @@ public TDFObject createTDF(InputStream payload, } TDFObject tdfObject = new TDFObject(); - tdfObject.prepareManifest(tdfConfig, kas); + tdfObject.prepareManifest(tdfConfig, services.kas()); long encryptedSegmentSize = tdfConfig.defaultSegmentSize + kGcmIvSize + kAesBlockSize; TDFWriter tdfWriter = new TDFWriter(outputStream); @@ -606,9 +591,16 @@ public TDFObject createTDF(InputStream payload, assertion.appliesToState = assertionConfig.appliesToState.toString(); var assertionHashAsHex = assertion.hash(); - var assertionHash = tdfConfig.hexEncodeRootAndSegmentHashes - ? assertionHashAsHex.getBytes(StandardCharsets.UTF_8) - : Hex.decodeHex(assertionHashAsHex); + byte[] assertionHash; + if (tdfConfig.hexEncodeRootAndSegmentHashes) { + assertionHash = assertionHashAsHex.getBytes(StandardCharsets.UTF_8); + } else { + try { + assertionHash = Hex.decodeHex(assertionHashAsHex); + } catch (DecoderException e) { + throw new SDKException("error decoding assertion hash", e); + } + } byte[] completeHash = new byte[aggregateHash.size() + assertionHash.length]; System.arraycopy(aggregateHash.toByteArray(), 0, completeHash, 0, aggregateHash.size()); System.arraycopy(assertionHash, 0, completeHash, aggregateHash.size(), assertionHash.length); @@ -624,7 +616,11 @@ public TDFObject createTDF(InputStream payload, assertionHashAsHex, encodedHash ); - assertion.sign(hashValues, assertionSigningKey); + try { + assertion.sign(hashValues, assertionSigningKey); + } catch (KeyLengthException e) { + throw new SDKException("error signing assertion hash", e); + } signedAssertions.add(assertion); } @@ -637,7 +633,7 @@ public TDFObject createTDF(InputStream payload, return tdfObject; } - public List defaultKases(TDFConfig config) { + static List defaultKases(TDFConfig config) { List allk = new ArrayList<>(); List defk = new ArrayList<>(); @@ -654,22 +650,23 @@ public List defaultKases(TDFConfig config) { return defk; } - public Reader loadTDF(SeekableByteChannel tdf, SDK.KAS kas) - throws DecoderException, IOException, ParseException, NoSuchAlgorithmException, JOSEException { - return loadTDF(tdf, kas, Config.newTDFReaderConfig()); - } - - public Reader loadTDF(SeekableByteChannel tdf, SDK.KAS kas, KeyAccessServerRegistryServiceFutureStub kasRegistryService, String platformUrl) - throws DecoderException, IOException, ParseException, NoSuchAlgorithmException, JOSEException, InterruptedException, ExecutionException, URISyntaxException { - return loadTDF(tdf, kas, Config.newTDFReaderConfig(), kasRegistryService, platformUrl); + Reader loadTDF(SeekableByteChannel tdf, String platformUrl) throws SDKException, IOException { + return loadTDF(tdf, Config.newTDFReaderConfig(), platformUrl); } - public Reader loadTDF(SeekableByteChannel tdf, SDK.KAS kas, Config.TDFReaderConfig tdfReaderConfig, KeyAccessServerRegistryServiceFutureStub kasRegistryService, String platformUrl) - throws DecoderException, IOException, ParseException, NoSuchAlgorithmException, JOSEException, InterruptedException, ExecutionException, URISyntaxException { + Reader loadTDF(SeekableByteChannel tdf, Config.TDFReaderConfig tdfReaderConfig, String platformUrl) throws SDKException, IOException { if (!tdfReaderConfig.ignoreKasAllowlist && (tdfReaderConfig.kasAllowlist == null || tdfReaderConfig.kasAllowlist.isEmpty())) { ListKeyAccessServersRequest request = ListKeyAccessServersRequest.newBuilder() .build(); - ListKeyAccessServersResponse response = kasRegistryService.listKeyAccessServers(request).get(); + ListKeyAccessServersResponse response; + try { + response = services.kasRegistry().listKeyAccessServers(request).get(); + } catch (ExecutionException e) { + throw new SDKException("error getting key access servers", e); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new SDKException("interrupted while getting list of kases", e); + } tdfReaderConfig.kasAllowlist = new HashSet<>(); for (var entry : response.getKeyAccessServersList()) { @@ -677,13 +674,10 @@ public Reader loadTDF(SeekableByteChannel tdf, SDK.KAS kas, Config.TDFReaderConf } tdfReaderConfig.kasAllowlist.add(Config.getKasAddress(platformUrl)); } - return loadTDF(tdf, kas, tdfReaderConfig); + return loadTDF(tdf, tdfReaderConfig); } - public Reader loadTDF(SeekableByteChannel tdf, SDK.KAS kas, - Config.TDFReaderConfig tdfReaderConfig) - throws RootSignatureValidationException, SegmentSizeMismatch, - IOException, FailedToCreateGMAC, JOSEException, ParseException, NoSuchAlgorithmException, DecoderException { + Reader loadTDF(SeekableByteChannel tdf, Config.TDFReaderConfig tdfReaderConfig) throws SDKException, IOException { TDFReader tdfReader = new TDFReader(tdf); String manifestJson = tdfReader.manifest(); @@ -696,7 +690,12 @@ public Reader loadTDF(SeekableByteChannel tdf, SDK.KAS kas, Set foundSplits = new HashSet<>(); Map skippedSplits = new HashMap<>(); - MessageDigest digest = MessageDigest.getInstance("SHA-256"); + MessageDigest digest = null; + try { + digest = MessageDigest.getInstance("SHA-256"); + } catch (NoSuchAlgorithmException e) { + throw new SDKException("error getting instance of SHA-256 digest", e); + } if (manifest.payload.isEncrypted) { for (Manifest.KeyAccess keyAccess : manifest.encryptionInformation.keyAccessObj) { @@ -718,7 +717,7 @@ public Reader loadTDF(SeekableByteChannel tdf, SDK.KAS kas, logger.error("KasAllowlist: kas url {} is not allowed", realAddress); throw new KasAllowlistException("KasAllowlist: kas url "+realAddress+" is not allowed"); } - unwrappedKey = kas.unwrap(keyAccess, manifest.encryptionInformation.policy, tdfReaderConfig.sessionKeyType); + unwrappedKey = services.kas().unwrap(keyAccess, manifest.encryptionInformation.policy, tdfReaderConfig.sessionKeyType); } catch (Exception e) { skippedSplits.put(ss, e); continue; @@ -825,14 +824,28 @@ public Reader loadTDF(SeekableByteChannel tdf, SDK.KAS kas, } } - var hashValues = assertion.verify(assertionKey); + Manifest.Assertion.HashValues hashValues = null; + try { + hashValues = assertion.verify(assertionKey); + } catch (ParseException | JOSEException e) { + throw new SDKException("error validating assertion hash", e); + } var hashOfAssertionAsHex = assertion.hash(); if (!Objects.equals(hashOfAssertionAsHex, hashValues.getAssertionHash())) { throw new AssertionException("assertion hash mismatch", assertion.id); } - byte[] hashOfAssertion = isLegacyTdf ? hashOfAssertionAsHex.getBytes(StandardCharsets.UTF_8) : Hex.decodeHex(hashOfAssertionAsHex); + byte[] hashOfAssertion; + if (isLegacyTdf) { + hashOfAssertion = hashOfAssertionAsHex.getBytes(StandardCharsets.UTF_8); + } else { + try { + hashOfAssertion = Hex.decodeHex(hashOfAssertionAsHex); + } catch (DecoderException e) { + throw new SDKException("error decoding assertion hash", e); + } + } var signature = new byte[aggregateHashByteArrayBytes.length + hashOfAssertion.length]; System.arraycopy(aggregateHashByteArrayBytes, 0, signature, 0, aggregateHashByteArrayBytes.length); System.arraycopy(hashOfAssertion, 0, signature, aggregateHashByteArrayBytes.length, hashOfAssertion.length); diff --git a/sdk/src/main/java/io/opentdf/platform/sdk/TDFReader.java b/sdk/src/main/java/io/opentdf/platform/sdk/TDFReader.java index 6691888a..61395075 100644 --- a/sdk/src/main/java/io/opentdf/platform/sdk/TDFReader.java +++ b/sdk/src/main/java/io/opentdf/platform/sdk/TDFReader.java @@ -7,6 +7,7 @@ import java.io.InputStreamReader; import java.nio.channels.SeekableByteChannel; import java.nio.charset.StandardCharsets; +import java.util.Map; import java.util.stream.Collectors; import static io.opentdf.platform.sdk.TDFWriter.TDF_MANIFEST_FILE_NAME; @@ -22,8 +23,8 @@ public class TDFReader { private final ZipReader.Entry manifestEntry; private final InputStream payload; - public TDFReader(SeekableByteChannel tdf) throws IOException { - var entries = new ZipReader(tdf).getEntries() + public TDFReader(SeekableByteChannel tdf) throws SDKException, IOException { + Map entries = new ZipReader(tdf).getEntries() .stream() .collect(Collectors.toMap(ZipReader.Entry::getName, e -> e)); diff --git a/sdk/src/test/java/io/opentdf/platform/sdk/FakeServices.java b/sdk/src/test/java/io/opentdf/platform/sdk/FakeServices.java new file mode 100644 index 00000000..dca1e6e1 --- /dev/null +++ b/sdk/src/test/java/io/opentdf/platform/sdk/FakeServices.java @@ -0,0 +1,78 @@ +package io.opentdf.platform.sdk; + +import io.opentdf.platform.authorization.AuthorizationServiceGrpc; +import io.opentdf.platform.policy.attributes.AttributesServiceGrpc; +import io.opentdf.platform.policy.kasregistry.KeyAccessServerRegistryServiceGrpc; +import io.opentdf.platform.policy.namespaces.NamespaceServiceGrpc; +import io.opentdf.platform.policy.resourcemapping.ResourceMappingServiceGrpc; +import io.opentdf.platform.policy.subjectmapping.SubjectMappingServiceGrpc; + +import java.util.Objects; + +public class FakeServices implements SDK.Services { + + private final AuthorizationServiceGrpc.AuthorizationServiceFutureStub authorizationService; + private final AttributesServiceGrpc.AttributesServiceFutureStub attributesService; + private final NamespaceServiceGrpc.NamespaceServiceFutureStub namespaceService; + private final SubjectMappingServiceGrpc.SubjectMappingServiceFutureStub subjectMappingService; + private final ResourceMappingServiceGrpc.ResourceMappingServiceFutureStub resourceMappingService; + private final KeyAccessServerRegistryServiceGrpc.KeyAccessServerRegistryServiceFutureStub keyAccessServerRegistryServiceFutureStub; + private final SDK.KAS kas; + + public FakeServices( + AuthorizationServiceGrpc.AuthorizationServiceFutureStub authorizationService, + AttributesServiceGrpc.AttributesServiceFutureStub attributesService, + NamespaceServiceGrpc.NamespaceServiceFutureStub namespaceService, + SubjectMappingServiceGrpc.SubjectMappingServiceFutureStub subjectMappingService, + ResourceMappingServiceGrpc.ResourceMappingServiceFutureStub resourceMappingService, + KeyAccessServerRegistryServiceGrpc.KeyAccessServerRegistryServiceFutureStub keyAccessServerRegistryServiceFutureStub, + SDK.KAS kas) { + this.authorizationService = authorizationService; + this.attributesService = attributesService; + this.namespaceService = namespaceService; + this.subjectMappingService = subjectMappingService; + this.resourceMappingService = resourceMappingService; + this.keyAccessServerRegistryServiceFutureStub = keyAccessServerRegistryServiceFutureStub; + this.kas = kas; + } + + @Override + public AuthorizationServiceGrpc.AuthorizationServiceFutureStub authorization() { + return Objects.requireNonNull(authorizationService); + } + + @Override + public AttributesServiceGrpc.AttributesServiceFutureStub attributes() { + return Objects.requireNonNull(attributesService); + } + + @Override + public NamespaceServiceGrpc.NamespaceServiceFutureStub namespaces() { + return Objects.requireNonNull(namespaceService); + } + + @Override + public SubjectMappingServiceGrpc.SubjectMappingServiceFutureStub subjectMappings() { + return Objects.requireNonNull(subjectMappingService); + } + + @Override + public ResourceMappingServiceGrpc.ResourceMappingServiceFutureStub resourceMappings() { + return Objects.requireNonNull(resourceMappingService); + } + + @Override + public KeyAccessServerRegistryServiceGrpc.KeyAccessServerRegistryServiceFutureStub kasRegistry() { + return Objects.requireNonNull(keyAccessServerRegistryServiceFutureStub); + } + + @Override + public SDK.KAS kas() { + return Objects.requireNonNull(kas); + } + + @Override + public void close() { + // no-op for this fake stuff in tests + } +} diff --git a/sdk/src/test/java/io/opentdf/platform/sdk/FakeServicesBuilder.java b/sdk/src/test/java/io/opentdf/platform/sdk/FakeServicesBuilder.java new file mode 100644 index 00000000..31e33087 --- /dev/null +++ b/sdk/src/test/java/io/opentdf/platform/sdk/FakeServicesBuilder.java @@ -0,0 +1,57 @@ +package io.opentdf.platform.sdk; + +import io.opentdf.platform.authorization.AuthorizationServiceGrpc; +import io.opentdf.platform.policy.attributes.AttributesServiceGrpc; +import io.opentdf.platform.policy.kasregistry.KeyAccessServerRegistryServiceGrpc; +import io.opentdf.platform.policy.namespaces.NamespaceServiceGrpc; +import io.opentdf.platform.policy.resourcemapping.ResourceMappingServiceGrpc; +import io.opentdf.platform.policy.subjectmapping.SubjectMappingServiceGrpc; + +public class FakeServicesBuilder { + private AuthorizationServiceGrpc.AuthorizationServiceFutureStub authorizationService; + private AttributesServiceGrpc.AttributesServiceFutureStub attributesService; + private NamespaceServiceGrpc.NamespaceServiceFutureStub namespaceService; + private SubjectMappingServiceGrpc.SubjectMappingServiceFutureStub subjectMappingService; + private ResourceMappingServiceGrpc.ResourceMappingServiceFutureStub resourceMappingService; + private KeyAccessServerRegistryServiceGrpc.KeyAccessServerRegistryServiceFutureStub keyAccessServerRegistryServiceFutureStub; + private SDK.KAS kas; + + public FakeServicesBuilder setAuthorizationService(AuthorizationServiceGrpc.AuthorizationServiceFutureStub authorizationService) { + this.authorizationService = authorizationService; + return this; + } + + public FakeServicesBuilder setAttributesService(AttributesServiceGrpc.AttributesServiceFutureStub attributesService) { + this.attributesService = attributesService; + return this; + } + + public FakeServicesBuilder setNamespaceService(NamespaceServiceGrpc.NamespaceServiceFutureStub namespaceService) { + this.namespaceService = namespaceService; + return this; + } + + public FakeServicesBuilder setSubjectMappingService(SubjectMappingServiceGrpc.SubjectMappingServiceFutureStub subjectMappingService) { + this.subjectMappingService = subjectMappingService; + return this; + } + + public FakeServicesBuilder setResourceMappingService(ResourceMappingServiceGrpc.ResourceMappingServiceFutureStub resourceMappingService) { + this.resourceMappingService = resourceMappingService; + return this; + } + + public FakeServicesBuilder setKeyAccessServerRegistryService(KeyAccessServerRegistryServiceGrpc.KeyAccessServerRegistryServiceFutureStub keyAccessServerRegistryServiceFutureStub) { + this.keyAccessServerRegistryServiceFutureStub = keyAccessServerRegistryServiceFutureStub; + return this; + } + + public FakeServicesBuilder setKas(SDK.KAS kas) { + this.kas = kas; + return this; + } + + public FakeServices build() { + return new FakeServices(authorizationService, attributesService, namespaceService, subjectMappingService, resourceMappingService, keyAccessServerRegistryServiceFutureStub, kas); + } +} \ No newline at end of file diff --git a/sdk/src/test/java/io/opentdf/platform/sdk/Fuzzing.java b/sdk/src/test/java/io/opentdf/platform/sdk/Fuzzing.java index 6b406038..12694121 100644 --- a/sdk/src/test/java/io/opentdf/platform/sdk/Fuzzing.java +++ b/sdk/src/test/java/io/opentdf/platform/sdk/Fuzzing.java @@ -2,18 +2,14 @@ import java.io.IOException; import java.io.OutputStream; -import java.net.URISyntaxException; import java.nio.ByteBuffer; import java.security.NoSuchAlgorithmException; -import java.text.ParseException; -import org.apache.commons.codec.DecoderException; import org.apache.commons.compress.utils.SeekableInMemoryByteChannel; import com.code_intelligence.jazzer.api.FuzzedDataProvider; import com.code_intelligence.jazzer.junit.FuzzTest; import com.google.gson.JsonParseException; -import com.nimbusds.jose.JOSEException; import io.opentdf.platform.sdk.TDF.FailedToCreateGMAC; import io.opentdf.platform.sdk.TDF.Reader; @@ -33,24 +29,24 @@ public void write(byte[] b, int off, int len) { }; @FuzzTest(maxDuration=TEST_DURATION) - public void fuzzNanoTDF(FuzzedDataProvider data) throws IOException, URISyntaxException { + public void fuzzNanoTDF(FuzzedDataProvider data) throws IOException { byte[] fuzzBytes = data.consumeRemainingAsBytes(); - NanoTDF nanoTDF = new NanoTDF(); - nanoTDF.readNanoTDF(ByteBuffer.wrap(fuzzBytes), IGNORE_OUTPUT_STREAM, NanoTDFTest.kas); + NanoTDF nanoTDF = new NanoTDF(new FakeServicesBuilder().setKas(NanoTDFTest.kas).build()); + nanoTDF.readNanoTDF(ByteBuffer.wrap(fuzzBytes), IGNORE_OUTPUT_STREAM); } @FuzzTest(maxDuration=TEST_DURATION) - public void fuzzTDF(FuzzedDataProvider data) throws FailedToCreateGMAC, NoSuchAlgorithmException, IOException, JOSEException, ParseException, DecoderException { + public void fuzzTDF(FuzzedDataProvider data) throws FailedToCreateGMAC, NoSuchAlgorithmException { byte[] fuzzBytes = data.consumeRemainingAsBytes(); byte[] key = new byte[32]; // use consistent zero key for performance and so fuzz can relate to seed var assertionVerificationKeys = new Config.AssertionVerificationKeys(); assertionVerificationKeys.defaultKey = new AssertionConfig.AssertionKey(AssertionConfig.AssertionKeyAlg.HS256, key); Config.TDFReaderConfig readerConfig = Config.newTDFReaderConfig( Config.withAssertionVerificationKeys(assertionVerificationKeys)); - TDF tdf = new TDF(); + TDF tdf = new TDF(new FakeServicesBuilder().setKas(TDFTest.kas).build()); try { - Reader reader = tdf.loadTDF(new SeekableInMemoryByteChannel(fuzzBytes), TDFTest.kas, readerConfig); + Reader reader = tdf.loadTDF(new SeekableInMemoryByteChannel(fuzzBytes), readerConfig); reader.readPayload(IGNORE_OUTPUT_STREAM); } catch (SDKException | InvalidZipException | JsonParseException | IOException | IllegalArgumentException e) { diff --git a/sdk/src/test/java/io/opentdf/platform/sdk/NanoTDFTest.java b/sdk/src/test/java/io/opentdf/platform/sdk/NanoTDFTest.java index e6bf21ca..2eeb4af3 100644 --- a/sdk/src/test/java/io/opentdf/platform/sdk/NanoTDFTest.java +++ b/sdk/src/test/java/io/opentdf/platform/sdk/NanoTDFTest.java @@ -163,13 +163,13 @@ void encryptionAndDecryptionWithValidKey() throws Exception { ByteBuffer byteBuffer = ByteBuffer.wrap(plainText.getBytes()); ByteArrayOutputStream tdfOutputStream = new ByteArrayOutputStream(); - NanoTDF nanoTDF = new NanoTDF(); - nanoTDF.createNanoTDF(byteBuffer, tdfOutputStream, config, kas); + NanoTDF nanoTDF = new NanoTDF(new FakeServicesBuilder().setKas(kas).setKeyAccessServerRegistryService(kasRegistryService).build()); + nanoTDF.createNanoTDF(byteBuffer, tdfOutputStream, config); byte[] nanoTDFBytes = tdfOutputStream.toByteArray(); ByteArrayOutputStream plainTextStream = new ByteArrayOutputStream(); - nanoTDF = new NanoTDF(); - nanoTDF.readNanoTDF(ByteBuffer.wrap(nanoTDFBytes), plainTextStream, kas, kasRegistryService, platformUrl); + nanoTDF = new NanoTDF(new FakeServicesBuilder().setKas(kas).setKeyAccessServerRegistryService(kasRegistryService).build()); + nanoTDF.readNanoTDF(ByteBuffer.wrap(nanoTDFBytes), plainTextStream, platformUrl); String out = new String(plainTextStream.toByteArray(), StandardCharsets.UTF_8); assertThat(out).isEqualTo(plainText); @@ -184,12 +184,12 @@ void encryptionAndDecryptionWithValidKey() throws Exception { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - NanoTDF nTDF = new NanoTDF(); - nTDF.createNanoTDF(ByteBuffer.wrap(data), outputStream, config, kas); + NanoTDF nTDF = new NanoTDF(new FakeServicesBuilder().setKas(kas).setKeyAccessServerRegistryService(kasRegistryService).build()); + nTDF.createNanoTDF(ByteBuffer.wrap(data), outputStream, config); byte[] nTDFBytes = outputStream.toByteArray(); ByteArrayOutputStream dataStream = new ByteArrayOutputStream(); - nanoTDF.readNanoTDF(ByteBuffer.wrap(nTDFBytes), dataStream, kas, kasRegistryService, platformUrl); + nanoTDF.readNanoTDF(ByteBuffer.wrap(nTDFBytes), dataStream, platformUrl); assertThat(dataStream.toByteArray()).isEqualTo(data); } } @@ -211,26 +211,26 @@ void runBasicTest(String kasUrl, boolean allowed, KeyAccessServerRegistryService ByteBuffer byteBuffer = ByteBuffer.wrap(plainText.getBytes()); ByteArrayOutputStream tdfOutputStream = new ByteArrayOutputStream(); - NanoTDF nanoTDF = new NanoTDF(); - nanoTDF.createNanoTDF(byteBuffer, tdfOutputStream, config, kas); + NanoTDF nanoTDF = new NanoTDF(new FakeServicesBuilder().setKas(kas).setKeyAccessServerRegistryService(kasReg).build()); + nanoTDF.createNanoTDF(byteBuffer, tdfOutputStream, config); byte[] nanoTDFBytes = tdfOutputStream.toByteArray(); ByteArrayOutputStream plainTextStream = new ByteArrayOutputStream(); - nanoTDF = new NanoTDF(); + nanoTDF = new NanoTDF(new FakeServicesBuilder().setKas(kas).setKeyAccessServerRegistryService(kasReg).build()); if (allowed) { if (decryptConfig != null) { - nanoTDF.readNanoTDF(ByteBuffer.wrap(nanoTDFBytes), plainTextStream, kas, decryptConfig); + nanoTDF.readNanoTDF(ByteBuffer.wrap(nanoTDFBytes), plainTextStream, decryptConfig); } else { - nanoTDF.readNanoTDF(ByteBuffer.wrap(nanoTDFBytes), plainTextStream, kas, kasReg, platformUrl); + nanoTDF.readNanoTDF(ByteBuffer.wrap(nanoTDFBytes), plainTextStream, platformUrl); } String out = new String(plainTextStream.toByteArray(), StandardCharsets.UTF_8); assertThat(out).isEqualTo(plainText); } else { try { if (decryptConfig != null) { - nanoTDF.readNanoTDF(ByteBuffer.wrap(nanoTDFBytes), plainTextStream, kas, decryptConfig); + nanoTDF.readNanoTDF(ByteBuffer.wrap(nanoTDFBytes), plainTextStream, decryptConfig); } else { - nanoTDF.readNanoTDF(ByteBuffer.wrap(nanoTDFBytes), plainTextStream, kas, kasReg, platformUrl); + nanoTDF.readNanoTDF(ByteBuffer.wrap(nanoTDFBytes), plainTextStream, platformUrl); } assertThat(false).isTrue(); } catch (SDKException e) { @@ -290,7 +290,7 @@ void collection() throws Exception { ByteBuffer byteBuffer = ByteBuffer.wrap(new byte[]{}); - NanoTDF nanoTDF = new NanoTDF(); + NanoTDF nanoTDF = new NanoTDF(new FakeServicesBuilder().setKas(kas).build()); ByteBuffer header = getHeaderBuffer(byteBuffer,nanoTDF, config); for (int i = 0; i < Config.MAX_COLLECTION_ITERATION - 10; i++) { config.collectionConfig.getHeaderInfo(); @@ -307,7 +307,7 @@ void collection() throws Exception { private ByteBuffer getHeaderBuffer(ByteBuffer input, NanoTDF nanoTDF, Config.NanoTDFConfig config) throws Exception { ByteArrayOutputStream tdfOutputStream = new ByteArrayOutputStream(); - nanoTDF.createNanoTDF(input, tdfOutputStream, config, kas); + nanoTDF.createNanoTDF(input, tdfOutputStream, config); ByteBuffer tdf = ByteBuffer.wrap(tdfOutputStream.toByteArray()); Header header = new Header(tdf); return tdf.position(0).slice().limit(header.getTotalSize()); 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 fb2441b7..ea79d77a 100644 --- a/sdk/src/test/java/io/opentdf/platform/sdk/TDFE2ETest.java +++ b/sdk/src/test/java/io/opentdf/platform/sdk/TDFE2ETest.java @@ -54,11 +54,11 @@ public void createAndDecryptTdfIT() throws Exception { InputStream plainTextInputStream = new ByteArrayInputStream(plainText.getBytes()); ByteArrayOutputStream tdfOutputStream = new ByteArrayOutputStream(); - TDF tdf = new TDF(); - tdf.createTDF(plainTextInputStream, tdfOutputStream, configPair.tdfConfig, sdk.getServices().kas(), sdk.getServices().attributes()); + TDF tdf = new TDF(sdk.getServices()); + tdf.createTDF(plainTextInputStream, tdfOutputStream, configPair.tdfConfig); var unwrappedData = new java.io.ByteArrayOutputStream(); - var reader = tdf.loadTDF(new SeekableInMemoryByteChannel(tdfOutputStream.toByteArray()), sdk.getServices().kas(), configPair.tdfReaderConfig, sdk.getServices().kasRegistry(), sdk.getPlatformUrl()); + var reader = tdf.loadTDF(new SeekableInMemoryByteChannel(tdfOutputStream.toByteArray()), configPair.tdfReaderConfig, sdk.getPlatformUrl()); reader.readPayload(unwrappedData); assertThat(unwrappedData.toString(StandardCharsets.UTF_8)).isEqualTo("text"); @@ -85,12 +85,12 @@ public void createAndDecryptNanoTDF() throws Exception { String plainText = "text"; ByteArrayOutputStream tdfOutputStream = new ByteArrayOutputStream(); - NanoTDF ntdf = new NanoTDF(); - ntdf.createNanoTDF(ByteBuffer.wrap(plainText.getBytes()), tdfOutputStream, config, sdk.kas()); + NanoTDF ntdf = new NanoTDF(sdk); + ntdf.createNanoTDF(ByteBuffer.wrap(plainText.getBytes()), tdfOutputStream, config); byte[] nanoTDFBytes = tdfOutputStream.toByteArray(); ByteArrayOutputStream plainTextStream = new ByteArrayOutputStream(); - ntdf.readNanoTDF(ByteBuffer.wrap(nanoTDFBytes), plainTextStream, sdk.kas()); + ntdf.readNanoTDF(ByteBuffer.wrap(nanoTDFBytes), plainTextStream); String out = new String(plainTextStream.toByteArray(), "UTF-8"); assertThat(out).isEqualTo("text"); 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 592694e6..8b1becc3 100644 --- a/sdk/src/test/java/io/opentdf/platform/sdk/TDFTest.java +++ b/sdk/src/test/java/io/opentdf/platform/sdk/TDFTest.java @@ -22,7 +22,6 @@ import java.io.OutputStream; import java.net.URISyntaxException; import java.nio.charset.StandardCharsets; -import java.security.Key; import java.security.KeyPair; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; @@ -210,12 +209,11 @@ public TDFConfigPair(Config.TDFConfig tdfConfig, Config.TDFReaderConfig tdfReade InputStream plainTextInputStream = new ByteArrayInputStream(plainText.getBytes()); ByteArrayOutputStream tdfOutputStream = new ByteArrayOutputStream(); - TDF tdf = new TDF(); - tdf.createTDF(plainTextInputStream, tdfOutputStream, configPair.tdfConfig, kas, null); + TDF tdf = new TDF(new FakeServicesBuilder().setKas(kas).setKeyAccessServerRegistryService(kasRegistryService).build()); + tdf.createTDF(plainTextInputStream, tdfOutputStream, configPair.tdfConfig); var unwrappedData = new ByteArrayOutputStream(); - var reader = tdf.loadTDF(new SeekableInMemoryByteChannel(tdfOutputStream.toByteArray()), kas, - configPair.tdfReaderConfig, kasRegistryService, platformUrl); + var reader = tdf.loadTDF(new SeekableInMemoryByteChannel(tdfOutputStream.toByteArray()), configPair.tdfReaderConfig, platformUrl); assertThat(reader.getManifest().payload.mimeType).isEqualTo("application/octet-stream"); reader.readPayload(unwrappedData); @@ -260,8 +258,8 @@ void testSimpleTDFWithAssertionWithRS256() throws Exception { InputStream plainTextInputStream = new ByteArrayInputStream(plainText.getBytes()); ByteArrayOutputStream tdfOutputStream = new ByteArrayOutputStream(); - TDF tdf = new TDF(); - tdf.createTDF(plainTextInputStream, tdfOutputStream, config, kas, null); + TDF tdf = new TDF(new FakeServicesBuilder().setKas(kas).setKeyAccessServerRegistryService(kasRegistryService).build()); + tdf.createTDF(plainTextInputStream, tdfOutputStream, config); var assertionVerificationKeys = new Config.AssertionVerificationKeys(); assertionVerificationKeys.keys.put(assertion1Id, @@ -270,8 +268,7 @@ void testSimpleTDFWithAssertionWithRS256() throws Exception { var unwrappedData = new ByteArrayOutputStream(); Config.TDFReaderConfig readerConfig = Config.newTDFReaderConfig( Config.withAssertionVerificationKeys(assertionVerificationKeys)); - var reader = tdf.loadTDF(new SeekableInMemoryByteChannel(tdfOutputStream.toByteArray()), kas, - readerConfig, kasRegistryService, platformUrl); + var reader = tdf.loadTDF(new SeekableInMemoryByteChannel(tdfOutputStream.toByteArray()), readerConfig, platformUrl); reader.readPayload(unwrappedData); assertThat(unwrappedData.toString(StandardCharsets.UTF_8)) @@ -304,24 +301,25 @@ void testWithAssertionVerificationDisabled() throws Exception { InputStream plainTextInputStream = new ByteArrayInputStream(plainText.getBytes()); ByteArrayOutputStream tdfOutputStream = new ByteArrayOutputStream(); - TDF tdf = new TDF(); - tdf.createTDF(plainTextInputStream, tdfOutputStream, config, kas, null); + TDF tdf = new TDF(new FakeServicesBuilder().setKas(kas).setKeyAccessServerRegistryService(kasRegistryService).build()); + tdf.createTDF(plainTextInputStream, tdfOutputStream, config); var assertionVerificationKeys = new Config.AssertionVerificationKeys(); assertionVerificationKeys.keys.put(assertion1Id, new AssertionConfig.AssertionKey(AssertionConfig.AssertionKeyAlg.RS256, keypair.getPublic())); var unwrappedData = new ByteArrayOutputStream(); - assertThrows(JOSEException.class, () -> { - tdf.loadTDF(new SeekableInMemoryByteChannel(tdfOutputStream.toByteArray()), kas, - Config.newTDFReaderConfig(), kasRegistryService, platformUrl); + var dataToUnwrap = new SeekableInMemoryByteChannel(tdfOutputStream.toByteArray()); + var emptyConfig= Config.newTDFReaderConfig(); + var thrown = assertThrows(SDKException.class, () -> { + tdf.loadTDF(dataToUnwrap, emptyConfig, platformUrl); }); + assertThat(thrown.getCause()).isInstanceOf(JOSEException.class); // try with assertion verification disabled and not passing the assertion verification keys Config.TDFReaderConfig readerConfig = Config.newTDFReaderConfig( Config.withDisableAssertionVerification(true)); - var reader = tdf.loadTDF(new SeekableInMemoryByteChannel(tdfOutputStream.toByteArray()), kas, - readerConfig, kasRegistryService, platformUrl); + var reader = tdf.loadTDF(new SeekableInMemoryByteChannel(tdfOutputStream.toByteArray()), readerConfig, platformUrl); reader.readPayload(unwrappedData); assertThat(unwrappedData.toString(StandardCharsets.UTF_8)) @@ -353,7 +351,7 @@ void testSimpleTDFWithAssertionWithHS256() throws Exception { assertionConfig2.statement.value = "{\"uuid\":\"f74efb60-4a9a-11ef-a6f1-8ee1a61c148a\",\"body\":{\"dataAttributes\":null,\"dissem\":null}}"; var rsaKasInfo = new Config.KASInfo(); - rsaKasInfo.URL = "https://example.com/kas"+Integer.toString(0); + rsaKasInfo.URL = "https://example.com/kas"+ 0; Config.TDFConfig config = Config.newTDFConfig( Config.withAutoconfigure(false), @@ -364,12 +362,11 @@ void testSimpleTDFWithAssertionWithHS256() throws Exception { InputStream plainTextInputStream = new ByteArrayInputStream(plainText.getBytes()); ByteArrayOutputStream tdfOutputStream = new ByteArrayOutputStream(); - TDF tdf = new TDF(); - tdf.createTDF(plainTextInputStream, tdfOutputStream, config, kas, null); + TDF tdf = new TDF(new FakeServicesBuilder().setKas(kas).setKeyAccessServerRegistryService(kasRegistryService).build()); + tdf.createTDF(plainTextInputStream, tdfOutputStream, config); var unwrappedData = new ByteArrayOutputStream(); - var reader = tdf.loadTDF(new SeekableInMemoryByteChannel(tdfOutputStream.toByteArray()), - kas, Config.newTDFReaderConfig(), kasRegistryService, platformUrl); + var reader = tdf.loadTDF(new SeekableInMemoryByteChannel(tdfOutputStream.toByteArray()), Config.newTDFReaderConfig(), platformUrl); reader.readPayload(unwrappedData); assertThat(unwrappedData.toString(StandardCharsets.UTF_8)) @@ -417,7 +414,7 @@ void testSimpleTDFWithAssertionWithHS256Failure() throws Exception { assertionConfig1.signingKey = new AssertionConfig.AssertionKey(AssertionConfig.AssertionKeyAlg.HS256, key); var rsaKasInfo = new Config.KASInfo(); - rsaKasInfo.URL = "https://example.com/kas"+Integer.toString(0); + rsaKasInfo.URL = "https://example.com/kas"+ 0; Config.TDFConfig config = Config.newTDFConfig( Config.withAutoconfigure(false), @@ -428,8 +425,8 @@ void testSimpleTDFWithAssertionWithHS256Failure() throws Exception { InputStream plainTextInputStream = new ByteArrayInputStream(plainText.getBytes()); ByteArrayOutputStream tdfOutputStream = new ByteArrayOutputStream(); - TDF tdf = new TDF(); - tdf.createTDF(plainTextInputStream, tdfOutputStream, config, kas, null); + TDF tdf = new TDF(new FakeServicesBuilder().setKas(kas).setKeyAccessServerRegistryService(kasRegistryService).build()); + tdf.createTDF(plainTextInputStream, tdfOutputStream, config); byte[] notkey = new byte[32]; secureRandom.nextBytes(notkey); @@ -439,10 +436,8 @@ void testSimpleTDFWithAssertionWithHS256Failure() throws Exception { Config.TDFReaderConfig readerConfig = Config.newTDFReaderConfig( Config.withAssertionVerificationKeys(assertionVerificationKeys)); - var unwrappedData = new ByteArrayOutputStream(); - Reader reader; try { - reader = tdf.loadTDF(new SeekableInMemoryByteChannel(tdfOutputStream.toByteArray()), kas, readerConfig, kasRegistryService, platformUrl); + tdf.loadTDF(new SeekableInMemoryByteChannel(tdfOutputStream.toByteArray()), readerConfig, platformUrl); throw new RuntimeException("assertion verify key error thrown"); } catch (SDKException e) { @@ -464,10 +459,10 @@ public void testCreatingTDFWithMultipleSegments() throws Exception { random.nextBytes(data); var plainTextInputStream = new ByteArrayInputStream(data); var tdfOutputStream = new ByteArrayOutputStream(); - var tdf = new TDF(); - tdf.createTDF(plainTextInputStream, tdfOutputStream, config, kas, null); + var tdf = new TDF(new FakeServicesBuilder().setKas(kas).setKeyAccessServerRegistryService(kasRegistryService).build()); + tdf.createTDF(plainTextInputStream, tdfOutputStream, config); var unwrappedData = new ByteArrayOutputStream(); - var reader = tdf.loadTDF(new SeekableInMemoryByteChannel(tdfOutputStream.toByteArray()), kas, kasRegistryService, platformUrl); + var reader = tdf.loadTDF(new SeekableInMemoryByteChannel(tdfOutputStream.toByteArray()), platformUrl); reader.readPayload(unwrappedData); assertThat(unwrappedData.toByteArray()) @@ -477,7 +472,7 @@ public void testCreatingTDFWithMultipleSegments() throws Exception { } @Test - public void testCreatingTooLargeTDF() throws Exception { + public void testCreatingTooLargeTDF() { var random = new Random(); var maxSize = random.nextInt(1024); var numReturned = new AtomicInteger(0); @@ -511,13 +506,13 @@ public void write(byte[] b, int off, int len) { } }; - var tdf = new TDF(maxSize); + var tdf = new TDF(maxSize, new FakeServicesBuilder().setKas(kas).build()); var tdfConfig = Config.newTDFConfig( Config.withAutoconfigure(false), Config.withKasInformation(getRSAKASInfos()), Config.withSegmentSize(Config.MIN_SEGMENT_SIZE)); assertThrows(TDF.DataSizeNotSupported.class, - () -> tdf.createTDF(is, os, tdfConfig, kas, null), + () -> tdf.createTDF(is, os, tdfConfig), "didn't throw an exception when we created TDF that was too large"); assertThat(numReturned.get()) .withFailMessage("test returned the wrong number of bytes") @@ -537,15 +532,15 @@ public void testCreateTDFWithMimeType() throws Exception { InputStream plainTextInputStream = new ByteArrayInputStream(plainText.getBytes()); ByteArrayOutputStream tdfOutputStream = new ByteArrayOutputStream(); - TDF tdf = new TDF(); - tdf.createTDF(plainTextInputStream, tdfOutputStream, config, kas, null); + TDF tdf = new TDF(new FakeServicesBuilder().setKas(kas).setKeyAccessServerRegistryService(kasRegistryService).build()); + tdf.createTDF(plainTextInputStream, tdfOutputStream, config); - var reader = tdf.loadTDF(new SeekableInMemoryByteChannel(tdfOutputStream.toByteArray()), kas, kasRegistryService, platformUrl); + var reader = tdf.loadTDF(new SeekableInMemoryByteChannel(tdfOutputStream.toByteArray()), platformUrl); assertThat(reader.getManifest().payload.mimeType).isEqualTo(mimeType); } @Test - public void legacyTDFRoundTrips() throws DecoderException, IOException, ExecutionException, JOSEException, InterruptedException, ParseException, NoSuchAlgorithmException, URISyntaxException { + public void legacyTDFRoundTrips() throws IOException, NoSuchAlgorithmException { final String mimeType = "application/pdf"; var assertionConfig1 = new AssertionConfig(); assertionConfig1.id = "assertion1"; @@ -569,12 +564,12 @@ public void legacyTDFRoundTrips() throws DecoderException, IOException, Executio InputStream plainTextInputStream = new ByteArrayInputStream(data); ByteArrayOutputStream tdfOutputStream = new ByteArrayOutputStream(); - TDF tdf = new TDF(); - tdf.createTDF(plainTextInputStream, tdfOutputStream, config, kas, null); + TDF tdf = new TDF(new FakeServicesBuilder().setKas(kas).setKeyAccessServerRegistryService(kasRegistryService).build()); + tdf.createTDF(plainTextInputStream, tdfOutputStream, config); var dataOutputStream = new ByteArrayOutputStream(); - var reader = tdf.loadTDF(new SeekableInMemoryByteChannel(tdfOutputStream.toByteArray()), kas, kasRegistryService, platformUrl); + var reader = tdf.loadTDF(new SeekableInMemoryByteChannel(tdfOutputStream.toByteArray()), platformUrl); var integrityInformation = reader.getManifest().encryptionInformation.integrityInformation; assertThat(reader.getManifest().tdfVersion).isNull(); var decodedSignature = Base64.getDecoder().decode(integrityInformation.rootSignature.signature); @@ -636,15 +631,14 @@ void testKasAllowlist() throws Exception { InputStream plainTextInputStream = new ByteArrayInputStream(plainText.getBytes()); ByteArrayOutputStream tdfOutputStream = new ByteArrayOutputStream(); - TDF tdf = new TDF(); - tdf.createTDF(plainTextInputStream, tdfOutputStream, config, kas, null); + TDF tdf = new TDF(new FakeServicesBuilder().setKas(kas).setKeyAccessServerRegistryService(kasRegistryServiceNoUrl).build()); + tdf.createTDF(plainTextInputStream, tdfOutputStream, config); var unwrappedData = new ByteArrayOutputStream(); - Reader reader; // should throw error because the kas url is not in the allowlist try { - reader = tdf.loadTDF(new SeekableInMemoryByteChannel(tdfOutputStream.toByteArray()), kas, Config.newTDFReaderConfig(), kasRegistryServiceNoUrl, platformUrl); + tdf.loadTDF(new SeekableInMemoryByteChannel(tdfOutputStream.toByteArray()), Config.newTDFReaderConfig(), platformUrl); throw new RuntimeException("expected allowlist error to be thrown"); } catch (Exception e) { assertThat(e).hasMessageContaining("KasAllowlist"); @@ -653,12 +647,12 @@ void testKasAllowlist() throws Exception { // with custom allowlist should succeed Config.TDFReaderConfig readerConfig = Config.newTDFReaderConfig( Config.WithKasAllowlist("https://example.com")); - reader = tdf.loadTDF(new SeekableInMemoryByteChannel(tdfOutputStream.toByteArray()), kas, readerConfig, kasRegistryServiceNoUrl, platformUrl); + tdf.loadTDF(new SeekableInMemoryByteChannel(tdfOutputStream.toByteArray()), readerConfig, platformUrl); // with ignore allowlist should succeed readerConfig = Config.newTDFReaderConfig( Config.WithIgnoreKasAllowlist(true)); - reader = tdf.loadTDF(new SeekableInMemoryByteChannel(tdfOutputStream.toByteArray()), kas, readerConfig, kasRegistryServiceNoUrl, platformUrl); + Reader reader = tdf.loadTDF(new SeekableInMemoryByteChannel(tdfOutputStream.toByteArray()), readerConfig, platformUrl); reader.readPayload(unwrappedData); assertThat(unwrappedData.toString(StandardCharsets.UTF_8)) @@ -674,11 +668,11 @@ void testKasAllowlist() throws Exception { Config.withKasInformation(platformKasInfo)); plainTextInputStream = new ByteArrayInputStream(plainText.getBytes()); tdfOutputStream = new ByteArrayOutputStream(); - tdf = new TDF(); - tdf.createTDF(plainTextInputStream, tdfOutputStream, config, kas, null); + tdf = new TDF(new FakeServicesBuilder().setKas(kas).setKeyAccessServerRegistryService(kasRegistryServiceNoUrl).build()); + tdf.createTDF(plainTextInputStream, tdfOutputStream, config); unwrappedData = new ByteArrayOutputStream(); - reader = tdf.loadTDF(new SeekableInMemoryByteChannel(tdfOutputStream.toByteArray()), kas, Config.newTDFReaderConfig(), kasRegistryServiceNoUrl, platformUrl); + reader = tdf.loadTDF(new SeekableInMemoryByteChannel(tdfOutputStream.toByteArray()), Config.newTDFReaderConfig(), platformUrl); reader.readPayload(unwrappedData); assertThat(unwrappedData.toString(StandardCharsets.UTF_8))