From 90bbbc505016c4edde402746352bd6d6e80c6433 Mon Sep 17 00:00:00 2001 From: Paul Flynn <43211074+pflynn-virtru@users.noreply.github.com> Date: Wed, 16 Oct 2024 14:49:42 -0400 Subject: [PATCH 01/14] ci: SonarCloud coverage (#195) Introduce a new Maven profile for JaCoCo test coverage reporting in the `pom.xml`. Additionally, update the GitHub Actions workflow to include a SonarCloud scan that uses the JaCoCo coverage data. DSP-119 DSP-120 --- .github/workflows/checks.yaml | 25 ++++++++++++++++-- pom.xml | 49 +++++++++++++++++++++++++++++++++++ sdk/pom.xml | 29 +++++++++++++++++++++ 3 files changed, 101 insertions(+), 2 deletions(-) diff --git a/.github/workflows/checks.yaml b/.github/workflows/checks.yaml index fa06679e..98b89f76 100644 --- a/.github/workflows/checks.yaml +++ b/.github/workflows/checks.yaml @@ -42,11 +42,31 @@ jobs: distribution: "adopt" server-id: github - name: Maven Verify - run: | - mvn --batch-mode verify + run: mvn --batch-mode verify + env: + BUF_INPUT_HTTPS_USERNAME: opentdf-bot + BUF_INPUT_HTTPS_PASSWORD: ${{ secrets.PERSONAL_ACCESS_TOKEN_OPENTDF }} + + sonarcloud: + name: SonarCloud Scan + runs-on: ubuntu-22.04 + steps: + - name: Check out repository + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 + - uses: bufbuild/buf-setup-action@2211e06e8cf26d628cda2eea15c95f8c42b080b3 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + - name: Set up JDK + uses: actions/setup-java@5896cecc08fd8a1fbdfaf517e29b571164b031f7 + with: + java-version: "17" + distribution: "temurin" + server-id: github + - name: Maven Test Coverage env: BUF_INPUT_HTTPS_USERNAME: opentdf-bot BUF_INPUT_HTTPS_PASSWORD: ${{ secrets.PERSONAL_ACCESS_TOKEN_OPENTDF }} + run: mvn --batch-mode clean verify -P coverage platform-integration: runs-on: ubuntu-22.04 @@ -257,6 +277,7 @@ jobs: - platform-integration - platform-xtest - mavenverify + - sonarcloud - pr runs-on: ubuntu-latest if: always() diff --git a/pom.xml b/pom.xml index 3307b7e1..4e186854 100644 --- a/pom.xml +++ b/pom.xml @@ -342,5 +342,54 @@ + + coverage + + sdk + + + opentdf + opentdf_java-sdk + https://sonarcloud.io + + + + + org.jacoco + jacoco-maven-plugin + 0.8.12 + + + prepare-agent + + prepare-agent + report-aggregate + + + + report + test + + report + + + ${project.parent.basedir}/target/jacoco.exec + + XML + + + + + post-test-report + prepare-package + + report + + + + + + + \ No newline at end of file diff --git a/sdk/pom.xml b/sdk/pom.xml index 890ba1e3..b6f06e1e 100644 --- a/sdk/pom.xml +++ b/sdk/pom.xml @@ -275,6 +275,35 @@ + + org.jacoco + jacoco-maven-plugin + 0.8.12 + + + prepare-agent + + prepare-agent + + + ${project.parent.basedir}/target/jacoco.exec + + + + report + test + + report + + + ${project.parent.basedir}/target/jacoco.exec + + XML + + + + + \ No newline at end of file From 210a1576290ceb2f356c7e6143a706176c6072ed Mon Sep 17 00:00:00 2001 From: Mike Jensen Date: Thu, 17 Oct 2024 08:26:42 -0600 Subject: [PATCH 02/14] chore(ci): Add initial CodeQL workflow configuration (#197) I want to explore using CodeQL to assist as part of our static analysis strategy. Because this tool is available for free to open source projects, opentdf is a good place to start this testing. This PR adds an initial configuration which will scan the codebase once a week. These results can be ignored for the time being. Instead the security team will review the results and make sure we tune away any noise first (or help in opening PR's to address any valid issues found). --- .github/workflows/codeql.yaml | 45 +++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 .github/workflows/codeql.yaml diff --git a/.github/workflows/codeql.yaml b/.github/workflows/codeql.yaml new file mode 100644 index 00000000..755b2183 --- /dev/null +++ b/.github/workflows/codeql.yaml @@ -0,0 +1,45 @@ +name: "CodeQL" + +on: + schedule: + - cron: '0 13 * * 1' # At 1:00 PM UTC every Monday + pull_request: + paths: + - '.github/workflows/codeql.yaml' + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: [ 'java' ] + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Buf setup + uses: bufbuild/buf-setup-action@2211e06e8cf26d628cda2eea15c95f8c42b080b3 + + - name: Initialize the CodeQL tools for scanning + uses: github/codeql-action/init@v3 + with: + languages: ${{ matrix.language }} + timeout-minutes: 5 + + - name: Autobuild + uses: github/codeql-action/autobuild@v3 + timeout-minutes: 10 + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v3 + with: + category: "/language:${{matrix.language}}" + timeout-minutes: 10 From 33c9513de68954cccba854d501ba26b62216df89 Mon Sep 17 00:00:00 2001 From: Paul Flynn <43211074+pflynn-virtru@users.noreply.github.com> Date: Fri, 18 Oct 2024 11:03:10 -0400 Subject: [PATCH 03/14] docs: JavaDoc (#196) Added JavaDoc comments to multiple classes and methods across the SDK, providing detailed descriptions of their functionality and usage. This enhances the readability and maintainability of the code. DSP-119 --- .../java/io/opentdf/platform/sdk/AesGcm.java | 5 +++++ .../opentdf/platform/sdk/AssertionConfig.java | 4 ++++ .../opentdf/platform/sdk/AsymDecryption.java | 3 +++ .../opentdf/platform/sdk/AsymEncryption.java | 4 ++++ .../platform/sdk/AutoConfigureException.java | 3 +++ .../opentdf/platform/sdk/Autoconfigure.java | 12 +++++++++- .../java/io/opentdf/platform/sdk/Config.java | 4 ++++ .../io/opentdf/platform/sdk/CryptoUtils.java | 3 +++ .../platform/sdk/InvalidZipException.java | 5 +++++ .../io/opentdf/platform/sdk/KASClient.java | 5 +++++ .../io/opentdf/platform/sdk/KASKeyCache.java | 22 +++++++++++++++++++ .../io/opentdf/platform/sdk/Manifest.java | 4 ++++ .../java/io/opentdf/platform/sdk/NanoTDF.java | 5 +++++ .../io/opentdf/platform/sdk/PolicyObject.java | 3 +++ .../java/io/opentdf/platform/sdk/SDK.java | 14 +++++++++++- .../io/opentdf/platform/sdk/SDKBuilder.java | 2 -- .../io/opentdf/platform/sdk/SDKException.java | 5 +++++ .../java/io/opentdf/platform/sdk/TDF.java | 13 +++++++++++ .../io/opentdf/platform/sdk/TDFReader.java | 5 +++++ .../io/opentdf/platform/sdk/TDFWriter.java | 4 ++++ .../io/opentdf/platform/sdk/ZipReader.java | 6 +++++ .../io/opentdf/platform/sdk/ZipWriter.java | 4 ++++ .../io/opentdf/platform/sdk/package-info.java | 19 ++++++++++++++++ 23 files changed, 150 insertions(+), 4 deletions(-) create mode 100644 sdk/src/main/java/io/opentdf/platform/sdk/package-info.java diff --git a/sdk/src/main/java/io/opentdf/platform/sdk/AesGcm.java b/sdk/src/main/java/io/opentdf/platform/sdk/AesGcm.java index 0d79c460..71445f69 100644 --- a/sdk/src/main/java/io/opentdf/platform/sdk/AesGcm.java +++ b/sdk/src/main/java/io/opentdf/platform/sdk/AesGcm.java @@ -12,6 +12,11 @@ import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; +/** + * The AesGcm class provides encryption and decryption methods using AES-GCM mode. + * It includes methods to encrypt and decrypt byte arrays using a specified + * symmetric key. + */ public class AesGcm { public static final int GCM_NONCE_LENGTH = 12; // in bytes public static final int GCM_TAG_LENGTH = 16; // in bytes diff --git a/sdk/src/main/java/io/opentdf/platform/sdk/AssertionConfig.java b/sdk/src/main/java/io/opentdf/platform/sdk/AssertionConfig.java index 510a008c..100f824f 100644 --- a/sdk/src/main/java/io/opentdf/platform/sdk/AssertionConfig.java +++ b/sdk/src/main/java/io/opentdf/platform/sdk/AssertionConfig.java @@ -3,6 +3,10 @@ import java.util.Objects; +/** + * Represents the configuration for assertions, encapsulating various types, scopes, states, keys, + * and statements involved in assertion handling. + */ public class AssertionConfig { public enum Type { diff --git a/sdk/src/main/java/io/opentdf/platform/sdk/AsymDecryption.java b/sdk/src/main/java/io/opentdf/platform/sdk/AsymDecryption.java index b7eb1e92..b76d1523 100644 --- a/sdk/src/main/java/io/opentdf/platform/sdk/AsymDecryption.java +++ b/sdk/src/main/java/io/opentdf/platform/sdk/AsymDecryption.java @@ -9,6 +9,9 @@ import java.security.spec.PKCS8EncodedKeySpec; import java.util.Base64; +/** + * Class providing functionality for asymmetric decryption using an RSA private key. + */ public class AsymDecryption { private final PrivateKey privateKey; private static final String PRIVATE_KEY_HEADER = "-----BEGIN PRIVATE KEY-----"; diff --git a/sdk/src/main/java/io/opentdf/platform/sdk/AsymEncryption.java b/sdk/src/main/java/io/opentdf/platform/sdk/AsymEncryption.java index 415fc375..3a81b1f5 100644 --- a/sdk/src/main/java/io/opentdf/platform/sdk/AsymEncryption.java +++ b/sdk/src/main/java/io/opentdf/platform/sdk/AsymEncryption.java @@ -15,6 +15,10 @@ import java.util.Base64; import java.util.Objects; +/** + * AsymEncryption class provides methods for asymmetric encryption and + * handling public keys in PEM format. + */ public class AsymEncryption { private final PublicKey publicKey; private static final String PUBLIC_KEY_HEADER = "-----BEGIN PUBLIC KEY-----"; 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 c134806c..157e6a8a 100644 --- a/sdk/src/main/java/io/opentdf/platform/sdk/AutoConfigureException.java +++ b/sdk/src/main/java/io/opentdf/platform/sdk/AutoConfigureException.java @@ -1,5 +1,8 @@ package io.opentdf.platform.sdk; +/** + * Exception thrown when automatic configuration fails. + */ public class AutoConfigureException extends RuntimeException { public AutoConfigureException(String message) { super(message); 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 372daee2..6113a681 100644 --- a/sdk/src/main/java/io/opentdf/platform/sdk/Autoconfigure.java +++ b/sdk/src/main/java/io/opentdf/platform/sdk/Autoconfigure.java @@ -35,7 +35,11 @@ import java.util.regex.Pattern; import java.util.stream.Collectors; -// Attribute rule types: operators! +/** + * The RuleType class defines a set of constants that represent various types of attribute rules. + * These constants are used to specify the nature and behavior of attribute rules in the context + * of key management and policy enforcement. + */ class RuleType { public static final String HIERARCHY = "hierarchy"; public static final String ALL_OF = "allOf"; @@ -44,6 +48,12 @@ class RuleType { public static final String EMPTY_TERM = "DEFAULT"; } +/** + * The Autoconfigure class provides methods for configuring and retrieving + * grants related to attribute values and KAS (Key Access Server) keys. + * This class includes functionality to create granter instances based on + * attributes either from a list of attribute values or from a service. + */ public class Autoconfigure { public static Logger logger = LoggerFactory.getLogger(Autoconfigure.class); 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 a8766820..c7d4f092 100644 --- a/sdk/src/main/java/io/opentdf/platform/sdk/Config.java +++ b/sdk/src/main/java/io/opentdf/platform/sdk/Config.java @@ -10,6 +10,10 @@ import java.util.*; import java.util.function.Consumer; +/** + * Configuration class for setting various configurations related to TDF. + * Contains nested classes and enums for specific configuration settings. + */ public class Config { public static final int TDF3_KEY_SIZE = 2048; diff --git a/sdk/src/main/java/io/opentdf/platform/sdk/CryptoUtils.java b/sdk/src/main/java/io/opentdf/platform/sdk/CryptoUtils.java index 6a53e7e5..404637df 100644 --- a/sdk/src/main/java/io/opentdf/platform/sdk/CryptoUtils.java +++ b/sdk/src/main/java/io/opentdf/platform/sdk/CryptoUtils.java @@ -5,6 +5,9 @@ import java.security.*; import java.util.Base64; +/** + * Utility class for cryptographic operations such as generating RSA key pairs and calculating HMAC. + */ public class CryptoUtils { private static final int KEYPAIR_SIZE = 2048; diff --git a/sdk/src/main/java/io/opentdf/platform/sdk/InvalidZipException.java b/sdk/src/main/java/io/opentdf/platform/sdk/InvalidZipException.java index e8ac4648..8bba58bd 100644 --- a/sdk/src/main/java/io/opentdf/platform/sdk/InvalidZipException.java +++ b/sdk/src/main/java/io/opentdf/platform/sdk/InvalidZipException.java @@ -1,5 +1,10 @@ package io.opentdf.platform.sdk; +/** + * InvalidZipException is thrown to indicate that a ZIP file being read + * is invalid or corrupted in some way. This exception extends RuntimeException, + * allowing it to be thrown during the normal operation of the Java Virtual Machine. + */ public class InvalidZipException extends RuntimeException { public InvalidZipException(String message) { super(message); diff --git a/sdk/src/main/java/io/opentdf/platform/sdk/KASClient.java b/sdk/src/main/java/io/opentdf/platform/sdk/KASClient.java index 4732865b..727eda83 100644 --- a/sdk/src/main/java/io/opentdf/platform/sdk/KASClient.java +++ b/sdk/src/main/java/io/opentdf/platform/sdk/KASClient.java @@ -30,6 +30,11 @@ import static java.lang.String.format; +/** + * A client implementation that communicates with a Key Access Service (KAS). + * This class provides methods to retrieve public keys, unwrap encrypted keys, + * and manage key caches. + */ public class KASClient implements SDK.KAS { private final Function channelFactory; diff --git a/sdk/src/main/java/io/opentdf/platform/sdk/KASKeyCache.java b/sdk/src/main/java/io/opentdf/platform/sdk/KASKeyCache.java index 22c4764c..5879dd05 100644 --- a/sdk/src/main/java/io/opentdf/platform/sdk/KASKeyCache.java +++ b/sdk/src/main/java/io/opentdf/platform/sdk/KASKeyCache.java @@ -8,6 +8,10 @@ import java.util.HashMap; import java.util.Map; +/** + * Class representing a cache for KAS (Key Access Server) information. + * It stores key information along with a timestamp to manage the freshness of cached data. + */ public class KASKeyCache { private static final Logger log = LoggerFactory.getLogger(KASKeyCache.class); Map cache; @@ -50,6 +54,17 @@ public void store(Config.KASInfo kasInfo) { } } +/** + * A class representing Key Aggregation Service (KAS) information along with a timestamp. + *

+ * This class holds information related to KAS, as well as a timestamp indicating when the + * information was recorded or generated. It encapsulates two main attributes: {@code kasInfo} + * and {@code timestamp}. + *

+ * The {@code kasInfo} field is an instance of {@code Config.KASInfo}, which contains the KAS-specific + * data. The {@code timestamp} field is an instance of {@code LocalDateTime}, representing + * the date and time when the KAS information was recorded. + */ class TimeStampedKASInfo { Config.KASInfo kasInfo; LocalDateTime timestamp; @@ -60,6 +75,13 @@ public TimeStampedKASInfo(Config.KASInfo kasInfo, LocalDateTime timestamp) { } } +/** + * Represents a request for a Key Access Server (KAS) key. + * This class is used to request keys using a specified URL and algorithm. + * + * This class also overrides equals and hashCode methods + * to ensure proper functioning within hash-based collections. + */ class KASKeyRequest { private String url; private String algorithm; diff --git a/sdk/src/main/java/io/opentdf/platform/sdk/Manifest.java b/sdk/src/main/java/io/opentdf/platform/sdk/Manifest.java index f9434c6b..bc4c4c3c 100644 --- a/sdk/src/main/java/io/opentdf/platform/sdk/Manifest.java +++ b/sdk/src/main/java/io/opentdf/platform/sdk/Manifest.java @@ -27,6 +27,10 @@ import java.util.List; import java.util.Objects; +/** + * The Manifest class represents a detailed structure encapsulating various aspects + * of data integrity, encryption, payload, and assertions within a certain context. + */ public class Manifest { private static final String kAssertionHash = "assertionHash"; 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 9132e570..75d0e734 100644 --- a/sdk/src/main/java/io/opentdf/platform/sdk/NanoTDF.java +++ b/sdk/src/main/java/io/opentdf/platform/sdk/NanoTDF.java @@ -16,6 +16,11 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +/** + * The NanoTDF class provides methods to create and read NanoTDF (Tiny Data Format) files. + * The NanoTDF format is intended for securely encrypting small data payloads using elliptic-curve cryptography + * and authenticated encryption. + */ public class NanoTDF { public static Logger logger = LoggerFactory.getLogger(NanoTDF.class); diff --git a/sdk/src/main/java/io/opentdf/platform/sdk/PolicyObject.java b/sdk/src/main/java/io/opentdf/platform/sdk/PolicyObject.java index 15d2d0bb..9d708699 100644 --- a/sdk/src/main/java/io/opentdf/platform/sdk/PolicyObject.java +++ b/sdk/src/main/java/io/opentdf/platform/sdk/PolicyObject.java @@ -2,6 +2,9 @@ import java.util.List; +/** + * The PolicyObject class represents a policy with a unique identifier and a body containing data attributes. + */ public class PolicyObject { static public class AttributeObject { public String attribute; 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 fe2a8005..14affa4c 100644 --- a/sdk/src/main/java/io/opentdf/platform/sdk/SDK.java +++ b/sdk/src/main/java/io/opentdf/platform/sdk/SDK.java @@ -34,11 +34,20 @@ public class SDK implements AutoCloseable { private static final Logger log = LoggerFactory.getLogger(SDK.class); + /** + * Closes the SDK, including its associated services. + * + * @throws Exception if an error occurs while closing the services. + */ @Override public void close() throws Exception { services.close(); } + /** + * KAS (Key Access Service) interface to define methods related to key access and management. + * This interface extends AutoCloseable to allow for resource management during close operations. + */ public interface KAS extends AutoCloseable { Config.KASInfo getPublicKey(Config.KASInfo kasInfo); @@ -51,7 +60,10 @@ public interface KAS extends AutoCloseable { KASKeyCache getKeyCache(); } - // TODO: add KAS + /** + * The Services interface provides access to various gRPC service stubs and a Key Authority Service (KAS). + * It extends the AutoCloseable interface, allowing for the release of resources when no longer needed. + */ public interface Services extends AutoCloseable { AuthorizationServiceFutureStub authorization(); diff --git a/sdk/src/main/java/io/opentdf/platform/sdk/SDKBuilder.java b/sdk/src/main/java/io/opentdf/platform/sdk/SDKBuilder.java index 4068b5e0..8c6b1804 100644 --- a/sdk/src/main/java/io/opentdf/platform/sdk/SDKBuilder.java +++ b/sdk/src/main/java/io/opentdf/platform/sdk/SDKBuilder.java @@ -69,7 +69,6 @@ public SDKBuilder sslFactory(SSLFactory sslFactory) { * Add SSL Context with trusted certs from certDirPath * * @param certsDirPath Path to a directory containing .pem or .crt trusted certs - * @return */ public SDKBuilder sslFactoryFromDirectory(String certsDirPath) throws Exception { File certsDir = new File(certsDirPath); @@ -91,7 +90,6 @@ public SDKBuilder sslFactoryFromDirectory(String certsDirPath) throws Exception * * @param keystorePath Path to keystore * @param keystorePassword Password to keystore - * @return */ public SDKBuilder sslFactoryFromKeyStore(String keystorePath, String keystorePassword) { this.sslFactory = SSLFactory.builder().withDefaultTrustMaterial().withSystemTrustMaterial() diff --git a/sdk/src/main/java/io/opentdf/platform/sdk/SDKException.java b/sdk/src/main/java/io/opentdf/platform/sdk/SDKException.java index 2d0b390d..58e7adbe 100644 --- a/sdk/src/main/java/io/opentdf/platform/sdk/SDKException.java +++ b/sdk/src/main/java/io/opentdf/platform/sdk/SDKException.java @@ -1,5 +1,10 @@ package io.opentdf.platform.sdk; +/** + * SDKException serves as a custom exception class for handling errors + * specific to the SDK's operations and processes. It extends + * RuntimeException, making it an unchecked exception. + */ public class SDKException extends RuntimeException { public SDKException(String message, Exception reason) { super(message, reason); 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 ba7c5b97..aec26e80 100644 --- a/sdk/src/main/java/io/opentdf/platform/sdk/TDF.java +++ b/sdk/src/main/java/io/opentdf/platform/sdk/TDF.java @@ -28,10 +28,23 @@ import java.util.*; import java.util.concurrent.ExecutionException; +/** + * The TDF class is responsible for handling operations related to + * Trusted Data Format (TDF). It includes methods to create and load + * TDF objects, as well as utility functions to handle cryptographic + * operations and configurations. + */ public class TDF { private final long maximumSize; + /** + * Constructs a new TDF instance using the default maximum input size defined by MAX_TDF_INPUT_SIZE. + *

+ * This constructor is primarily used to initialize the TDF object with the standard maximum + * 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); } 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 24b05940..6691888a 100644 --- a/sdk/src/main/java/io/opentdf/platform/sdk/TDFReader.java +++ b/sdk/src/main/java/io/opentdf/platform/sdk/TDFReader.java @@ -12,6 +12,11 @@ import static io.opentdf.platform.sdk.TDFWriter.TDF_MANIFEST_FILE_NAME; import static io.opentdf.platform.sdk.TDFWriter.TDF_PAYLOAD_FILE_NAME; +/** + * TDFReader is responsible for reading and processing Trusted Data Format (TDF) files. + * The class initializes with a TDF file channel, extracts the manifest and payload entries, + * and provides methods to retrieve the manifest content, read payload bytes, and read policy objects. + */ public class TDFReader { private final ZipReader.Entry manifestEntry; diff --git a/sdk/src/main/java/io/opentdf/platform/sdk/TDFWriter.java b/sdk/src/main/java/io/opentdf/platform/sdk/TDFWriter.java index a5f3509b..048822f6 100644 --- a/sdk/src/main/java/io/opentdf/platform/sdk/TDFWriter.java +++ b/sdk/src/main/java/io/opentdf/platform/sdk/TDFWriter.java @@ -4,6 +4,10 @@ import java.io.OutputStream; import java.nio.charset.StandardCharsets; +/** + * The TDFWriter class provides functionalities for creating a TDF (Trusted Data Format) archive. + * This includes appending a manifest file and appending payload data to the archive. + */ public class TDFWriter { public static final String TDF_PAYLOAD_FILE_NAME = "0.payload"; public static final String TDF_MANIFEST_FILE_NAME = "0.manifest.json"; diff --git a/sdk/src/main/java/io/opentdf/platform/sdk/ZipReader.java b/sdk/src/main/java/io/opentdf/platform/sdk/ZipReader.java index 17bc51c5..cb6d5ffd 100644 --- a/sdk/src/main/java/io/opentdf/platform/sdk/ZipReader.java +++ b/sdk/src/main/java/io/opentdf/platform/sdk/ZipReader.java @@ -12,6 +12,12 @@ import java.util.ArrayList; import java.util.List; +/** + * The ZipReader class provides functionality to read basic ZIP file + * structures, such as the End of Central Directory Record and the + * Local File Header. This class supports standard ZIP archives as well + * as ZIP64 format. + */ public class ZipReader { public static final Logger logger = LoggerFactory.getLogger(ZipReader.class); diff --git a/sdk/src/main/java/io/opentdf/platform/sdk/ZipWriter.java b/sdk/src/main/java/io/opentdf/platform/sdk/ZipWriter.java index d775a0a8..71aea34c 100644 --- a/sdk/src/main/java/io/opentdf/platform/sdk/ZipWriter.java +++ b/sdk/src/main/java/io/opentdf/platform/sdk/ZipWriter.java @@ -9,6 +9,10 @@ import java.util.ArrayList; import java.util.zip.CRC32; +/** + * The ZipWriter class provides functionalities to create ZIP archive files. + * It writes files and data to an underlying output stream in the ZIP file format. + */ public class ZipWriter { private static final int ZIP_VERSION = 0x2D; diff --git a/sdk/src/main/java/io/opentdf/platform/sdk/package-info.java b/sdk/src/main/java/io/opentdf/platform/sdk/package-info.java new file mode 100644 index 00000000..cd3ae44a --- /dev/null +++ b/sdk/src/main/java/io/opentdf/platform/sdk/package-info.java @@ -0,0 +1,19 @@ +/** + * The io.opentdf.platform.sdk package provides a comprehensive set of + * classes, interfaces, enums, and exceptions designed for interacting with + * the OpenTDF platform. At its core, the {@link io.opentdf.platform.sdk.SDK} class + * serves as the centerpiece for this package, representing a software development kit + * that facilitates integration with the OpenTDF services. The package also includes + * classes for various encryption and decryption operations such as {@link io.opentdf.platform.sdk.AesGcm} + * and its inner classes, as well as support for assertions and configurations through + * classes like {@link io.opentdf.platform.sdk.AssertionConfig} and {@link io.opentdf.platform.sdk.Config}. + * Additionally, utility classes such as {@link io.opentdf.platform.sdk.CryptoUtils} and + * functionality for reading and writing TDF files are provided via {@link io.opentdf.platform.sdk.TDFReader} + * and {@link io.opentdf.platform.sdk.TDFWriter}. The package also defines a collection of + * enums for various configurations and state definitions, alongside a comprehensive + * set of exceptions to handle different error scenarios, ensuring robust and secure + * operations within the OpenTDF ecosystem. + * + * @since 1.0 + */ +package io.opentdf.platform.sdk; \ No newline at end of file From dcfbb56d39a6f3a634df1ba6ae030c4be8e68d28 Mon Sep 17 00:00:00 2001 From: Paul Flynn <43211074+pflynn-virtru@users.noreply.github.com> Date: Mon, 21 Oct 2024 13:48:05 -0400 Subject: [PATCH 04/14] ci: coverage report fix (#198) Configured the Jacoco plugin to specify the output directory for report generation. This change ensures that the report is consistently placed in the target directory for easier access and management. --- .github/workflows/checks.yaml | 18 +++++++++++++++++- sdk/pom.xml | 1 + 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/.github/workflows/checks.yaml b/.github/workflows/checks.yaml index 98b89f76..91e4e99b 100644 --- a/.github/workflows/checks.yaml +++ b/.github/workflows/checks.yaml @@ -53,6 +53,8 @@ jobs: steps: - name: Check out repository uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 + with: + fetch-depth: 0 - uses: bufbuild/buf-setup-action@2211e06e8cf26d628cda2eea15c95f8c42b080b3 with: github_token: ${{ secrets.GITHUB_TOKEN }} @@ -62,11 +64,25 @@ jobs: java-version: "17" distribution: "temurin" server-id: github + - name: Cache SonarCloud packages + uses: actions/cache@v4 + with: + path: ~/.sonar/cache + key: ${{ runner.os }}-sonar + restore-keys: ${{ runner.os }}-sonar + - name: Cache Maven packages + uses: actions/cache@v4 + with: + path: ~/.m2 + key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} + restore-keys: ${{ runner.os }}-m2 - name: Maven Test Coverage env: + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} BUF_INPUT_HTTPS_USERNAME: opentdf-bot BUF_INPUT_HTTPS_PASSWORD: ${{ secrets.PERSONAL_ACCESS_TOKEN_OPENTDF }} - run: mvn --batch-mode clean verify -P coverage + run: mvn --batch-mode clean verify org.sonarsource.scanner.maven:sonar-maven-plugin:sonar -Dsonar.projectKey=opentdf_java-sdk -P coverage platform-integration: runs-on: ubuntu-22.04 diff --git a/sdk/pom.xml b/sdk/pom.xml index b6f06e1e..0e608949 100644 --- a/sdk/pom.xml +++ b/sdk/pom.xml @@ -297,6 +297,7 @@ ${project.parent.basedir}/target/jacoco.exec + ${project.parent.basedir}/target/site/jacoco/ XML From 1dffd35374c40ebaa095594d2a5db138957c6e38 Mon Sep 17 00:00:00 2001 From: sujankota Date: Thu, 24 Oct 2024 11:13:08 -0400 Subject: [PATCH 05/14] fix(sdk): returns the correct string associated with enums (#200) --- .../io/opentdf/platform/sdk/AssertionConfig.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/sdk/src/main/java/io/opentdf/platform/sdk/AssertionConfig.java b/sdk/src/main/java/io/opentdf/platform/sdk/AssertionConfig.java index 100f824f..c31fc1b0 100644 --- a/sdk/src/main/java/io/opentdf/platform/sdk/AssertionConfig.java +++ b/sdk/src/main/java/io/opentdf/platform/sdk/AssertionConfig.java @@ -34,6 +34,11 @@ public enum Scope { Scope(String scope) { this.scope = scope; } + + @Override + public String toString() { + return scope; + } } public enum AssertionKeyAlg { @@ -51,6 +56,11 @@ public enum AppliesToState { AppliesToState(String state) { this.state = state; } + + @Override + public String toString() { + return state; + } } public enum BindingMethod { @@ -61,6 +71,11 @@ public enum BindingMethod { BindingMethod(String method) { this.method = method; } + + @Override + public String toString() { + return method; + } } static public class AssertionKey { From 4084f44b31830b3385dcc641d1388fe2c769bd28 Mon Sep 17 00:00:00 2001 From: "opentdf-automation[bot]" <149537512+opentdf-automation[bot]@users.noreply.github.com> Date: Thu, 24 Oct 2024 15:40:56 -0400 Subject: [PATCH 06/14] chore(main): release 0.7.4 (#194) :robot: I have created a release *beep* *boop* ---

0.7.4 ## [0.7.4](https://github.com/opentdf/java-sdk/compare/v0.7.3...v0.7.4) (2024-10-24) ### Bug Fixes * **sdk:** returns the correct string associated with enums ([#200](https://github.com/opentdf/java-sdk/issues/200)) ([1dffd35](https://github.com/opentdf/java-sdk/commit/1dffd35374c40ebaa095594d2a5db138957c6e38)) ### Documentation * JavaDoc ([#196](https://github.com/opentdf/java-sdk/issues/196)) ([33c9513](https://github.com/opentdf/java-sdk/commit/33c9513de68954cccba854d501ba26b62216df89)) * minor Java SDK README updates ([#193](https://github.com/opentdf/java-sdk/issues/193)) ([e9dc738](https://github.com/opentdf/java-sdk/commit/e9dc738cc40ffc97d3f0084086b1afa1c283850c))
--- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). Co-authored-by: opentdf-automation[bot] <149537512+opentdf-automation[bot]@users.noreply.github.com> --- .release-please-manifest.json | 2 +- CHANGELOG.md | 13 +++++++++++++ cmdline/pom.xml | 2 +- pom.xml | 2 +- sdk/pom.xml | 2 +- 5 files changed, 17 insertions(+), 4 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 88ca3ece..904b037e 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.7.3" + ".": "0.7.4" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index d54306d4..453b346e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # Changelog +## [0.7.4](https://github.com/opentdf/java-sdk/compare/v0.7.3...v0.7.4) (2024-10-24) + + +### Bug Fixes + +* **sdk:** returns the correct string associated with enums ([#200](https://github.com/opentdf/java-sdk/issues/200)) ([1dffd35](https://github.com/opentdf/java-sdk/commit/1dffd35374c40ebaa095594d2a5db138957c6e38)) + + +### Documentation + +* JavaDoc ([#196](https://github.com/opentdf/java-sdk/issues/196)) ([33c9513](https://github.com/opentdf/java-sdk/commit/33c9513de68954cccba854d501ba26b62216df89)) +* minor Java SDK README updates ([#193](https://github.com/opentdf/java-sdk/issues/193)) ([e9dc738](https://github.com/opentdf/java-sdk/commit/e9dc738cc40ffc97d3f0084086b1afa1c283850c)) + ## [0.7.3](https://github.com/opentdf/java-sdk/compare/v0.7.2...v0.7.3) (2024-10-09) diff --git a/cmdline/pom.xml b/cmdline/pom.xml index 705455f6..897f5658 100644 --- a/cmdline/pom.xml +++ b/cmdline/pom.xml @@ -4,7 +4,7 @@ io.opentdf.platform sdk-pom - 0.7.4-SNAPSHOT + 0.7.4 cmdline diff --git a/pom.xml b/pom.xml index 4e186854..30fe54e6 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ io.opentdf.platform sdk-pom - 0.7.4-SNAPSHOT + 0.7.4 io.opentdf.platform:sdk-pom OpenTDF Java SDK https://github.com/opentdf/java-sdk diff --git a/sdk/pom.xml b/sdk/pom.xml index 0e608949..db01dbc2 100644 --- a/sdk/pom.xml +++ b/sdk/pom.xml @@ -6,7 +6,7 @@ sdk-pom io.opentdf.platform - 0.7.4-SNAPSHOT + 0.7.4 jar From ac13a0a7c82caed920238244cf7adaca3039fdea Mon Sep 17 00:00:00 2001 From: Elizabeth Healy <35498075+elizabethhealy@users.noreply.github.com> Date: Mon, 28 Oct 2024 14:22:18 -0400 Subject: [PATCH 07/14] feat: Examples module (#202) The examples that we provide in the docs repo Add a module to manage them here Compile in ci to ensure there are no breaking changes to docs --- examples/buf.gen.yaml | 14 ++ examples/buf.lock | 18 ++ examples/buf.yaml | 22 +++ examples/pom.xml | 179 ++++++++++++++++++ .../main/java/io/opentdf/platform/App.java | 7 + .../io/opentdf/platform/CreateAttribute.java | 36 ++++ .../io/opentdf/platform/CreateNamespace.java | 27 +++ .../platform/CreateSubjectConditionSet.java | 47 +++++ .../platform/CreateSubjectMapping.java | 34 ++++ .../io/opentdf/platform/DecryptExample.java | 42 ++++ .../io/opentdf/platform/EncryptExample.java | 39 ++++ .../io/opentdf/platform/GetDecisions.java | 37 ++++ .../io/opentdf/platform/GetEntitlements.java | 32 ++++ .../io/opentdf/platform/ListAttributes.java | 34 ++++ .../io/opentdf/platform/ListNamespaces.java | 27 +++ .../opentdf/platform/ListSubjectMappings.java | 32 ++++ examples/src/main/resources/log4j2.xml | 16 ++ pom.xml | 1 + 18 files changed, 644 insertions(+) create mode 100644 examples/buf.gen.yaml create mode 100644 examples/buf.lock create mode 100644 examples/buf.yaml create mode 100644 examples/pom.xml create mode 100644 examples/src/main/java/io/opentdf/platform/App.java create mode 100644 examples/src/main/java/io/opentdf/platform/CreateAttribute.java create mode 100644 examples/src/main/java/io/opentdf/platform/CreateNamespace.java create mode 100644 examples/src/main/java/io/opentdf/platform/CreateSubjectConditionSet.java create mode 100644 examples/src/main/java/io/opentdf/platform/CreateSubjectMapping.java create mode 100644 examples/src/main/java/io/opentdf/platform/DecryptExample.java create mode 100644 examples/src/main/java/io/opentdf/platform/EncryptExample.java create mode 100644 examples/src/main/java/io/opentdf/platform/GetDecisions.java create mode 100644 examples/src/main/java/io/opentdf/platform/GetEntitlements.java create mode 100644 examples/src/main/java/io/opentdf/platform/ListAttributes.java create mode 100644 examples/src/main/java/io/opentdf/platform/ListNamespaces.java create mode 100644 examples/src/main/java/io/opentdf/platform/ListSubjectMappings.java create mode 100644 examples/src/main/resources/log4j2.xml diff --git a/examples/buf.gen.yaml b/examples/buf.gen.yaml new file mode 100644 index 00000000..6bb9c4e3 --- /dev/null +++ b/examples/buf.gen.yaml @@ -0,0 +1,14 @@ +version: v1 +managed: + enabled: true + java_package_prefix: + default: io.opentdf.platform + except: + - buf.build/bufbuild/protovalidate + - buf.build/googleapis/googleapis + - buf.build/grpc-ecosystem/grpc-gateway +plugins: + - plugin: buf.build/protocolbuffers/java:v25.3 + out: ./ + - plugin: buf.build/grpc/java:v1.61.1 + out: ./ diff --git a/examples/buf.lock b/examples/buf.lock new file mode 100644 index 00000000..deef61e8 --- /dev/null +++ b/examples/buf.lock @@ -0,0 +1,18 @@ +# Generated by buf. DO NOT EDIT. +version: v1 +deps: + - remote: buf.build + owner: bufbuild + repository: protovalidate + commit: f05a6f4403ce4327bae4f50f281c3ed0 + digest: shake256:668a0661b8df44d41839194896329330965fc215f3d2f88057fd60eeb759c2daf6cc6edfdd13b2a653d49fe2896ebedcb1a33c4c5b2dd10919f03ffb7fc52ae6 + - remote: buf.build + owner: googleapis + repository: googleapis + commit: 7e6f6e774e29406da95bd61cdcdbc8bc + digest: shake256:fe43dd2265ea0c07d76bd925eeba612667cf4c948d2ce53d6e367e1b4b3cb5fa69a51e6acb1a6a50d32f894f054a35e6c0406f6808a483f2752e10c866ffbf73 + - remote: buf.build + owner: grpc-ecosystem + repository: grpc-gateway + commit: 3f42134f4c564983838425bc43c7a65f + digest: shake256:3d11d4c0fe5e05fda0131afefbce233940e27f0c31c5d4e385686aea58ccd30f72053f61af432fa83f1fc11cda57f5f18ca3da26a29064f73c5a0d076bba8d92 \ No newline at end of file diff --git a/examples/buf.yaml b/examples/buf.yaml new file mode 100644 index 00000000..2dc8eb0e --- /dev/null +++ b/examples/buf.yaml @@ -0,0 +1,22 @@ +version: v1 +deps: + - buf.build/bufbuild/protovalidate + - buf.build/googleapis/googleapis + - buf.build/grpc-ecosystem/grpc-gateway +breaking: + use: + - FILE + - PACKAGE + - WIRE_JSON + - WIRE +lint: + allow_comment_ignores: true + use: + - DEFAULT + except: + - PACKAGE_VERSION_SUFFIX + ignore_only: + PACKAGE_VERSION_SUFFIX: + - google/api/annotations.proto + - google/api/http.proto + - google/protobuf/wrappers.proto diff --git a/examples/pom.xml b/examples/pom.xml new file mode 100644 index 00000000..28915c44 --- /dev/null +++ b/examples/pom.xml @@ -0,0 +1,179 @@ + + + 4.0.0 + + sdk-pom + io.opentdf.platform + 0.7.4 + + + io.opentdf.platform + examples + 0.7.4 + + examples + + http://www.example.com + + + UTF-8 + 11 + + + + + + org.junit + junit-bom + 5.11.0 + pom + import + + + + + + + org.junit.jupiter + junit-jupiter-api + test + + + + org.junit.jupiter + junit-jupiter-params + test + + + io.opentdf.platform + sdk + ${project.version} + + + com.google.code.gson + gson + 2.11.0 + + + org.apache.logging.log4j + log4j-slf4j2-impl + + + org.apache.logging.log4j + log4j-core + + + org.apache.logging.log4j + log4j-api + + + javax.annotation + javax.annotation-api + 1.3.2 + + + + + + + + org.apache.maven.plugins + maven-shade-plugin + 3.2.4 + + + package + + shade + + + false + + + io.opentdf.platform.App + + ${project.version} + io.opentdf.platform.App + + + + + + + *:* + + META-INF/*.SF + META-INF/*.DSA + META-INF/*.RSA + + + + + + + + + org.codehaus.mojo + exec-maven-plugin + 3.0.0 + + io.opentdf.platform.App + + + + org.apache.maven.plugins + maven-antrun-plugin + 3.1.0 + + + + generateSources + generate-sources + + + + + + + + + + + + + + + run + + + + + + + org.codehaus.mojo + build-helper-maven-plugin + 3.5.0 + + + add-source + generate-sources + + add-source + + + + target/generated-sources + + + + + + + + + diff --git a/examples/src/main/java/io/opentdf/platform/App.java b/examples/src/main/java/io/opentdf/platform/App.java new file mode 100644 index 00000000..22958126 --- /dev/null +++ b/examples/src/main/java/io/opentdf/platform/App.java @@ -0,0 +1,7 @@ +package io.opentdf.platform; + +public class App { + public static void main(String[] args) { + System.out.println("opentdf examples"); + } +} diff --git a/examples/src/main/java/io/opentdf/platform/CreateAttribute.java b/examples/src/main/java/io/opentdf/platform/CreateAttribute.java new file mode 100644 index 00000000..ba72189e --- /dev/null +++ b/examples/src/main/java/io/opentdf/platform/CreateAttribute.java @@ -0,0 +1,36 @@ +package io.opentdf.platform; +import io.opentdf.platform.sdk.*; + +import java.util.concurrent.ExecutionException; + +import io.opentdf.platform.policy.AttributeRuleTypeEnum; + +import io.opentdf.platform.policy.attributes.*; +import io.opentdf.platform.policy.Attribute; + +import java.util.Arrays; + +public class CreateAttribute { + public static void main(String[] args) throws ExecutionException, InterruptedException{ + + String clientId = "opentdf"; + String clientSecret = "secret"; + String platformEndpoint = "localhost:8080"; + + SDKBuilder builder = new SDKBuilder(); + SDK sdk = builder.platformEndpoint(platformEndpoint) + .clientSecret(clientId, clientSecret).useInsecurePlaintextConnection(true) + .build(); + + CreateAttributeRequest request = CreateAttributeRequest.newBuilder() + .setNamespaceId("877990d1-609b-42ab-a273-4253b8b321eb") + .setName("test") + .setRule(AttributeRuleTypeEnum.forNumber(AttributeRuleTypeEnum.ATTRIBUTE_RULE_TYPE_ENUM_ALL_OF_VALUE)) + .addAllValues(Arrays.asList("test1", "test2")).build(); + + CreateAttributeResponse resp = sdk.getServices().attributes().createAttribute(request).get(); + + Attribute attribute = resp.getAttribute(); + + } +} diff --git a/examples/src/main/java/io/opentdf/platform/CreateNamespace.java b/examples/src/main/java/io/opentdf/platform/CreateNamespace.java new file mode 100644 index 00000000..112cde4f --- /dev/null +++ b/examples/src/main/java/io/opentdf/platform/CreateNamespace.java @@ -0,0 +1,27 @@ +package io.opentdf.platform; +import io.opentdf.platform.sdk.*; + +import java.util.concurrent.ExecutionException; + +import io.opentdf.platform.policy.namespaces.*; + +public class CreateNamespace { + public static void main(String[] args) throws ExecutionException, InterruptedException{ + + String clientId = "opentdf"; + String clientSecret = "secret"; + String platformEndpoint = "localhost:8080"; + + SDKBuilder builder = new SDKBuilder(); + SDK sdk = builder.platformEndpoint(platformEndpoint) + .clientSecret(clientId, clientSecret).useInsecurePlaintextConnection(true) + .build(); + + CreateNamespaceRequest request = CreateNamespaceRequest.newBuilder().setName("mynamespace.com").build(); + + CreateNamespaceResponse resp = sdk.getServices().namespaces().createNamespace(request).get(); + + System.out.println(resp.getNamespace().getName()); + + } +} diff --git a/examples/src/main/java/io/opentdf/platform/CreateSubjectConditionSet.java b/examples/src/main/java/io/opentdf/platform/CreateSubjectConditionSet.java new file mode 100644 index 00000000..0eb6c5a2 --- /dev/null +++ b/examples/src/main/java/io/opentdf/platform/CreateSubjectConditionSet.java @@ -0,0 +1,47 @@ +package io.opentdf.platform; +import io.opentdf.platform.sdk.*; + +import java.util.concurrent.ExecutionException; + +import io.opentdf.platform.policy.subjectmapping.*; +import io.opentdf.platform.policy.SubjectMapping; +import io.opentdf.platform.policy.SubjectConditionSet; +import io.opentdf.platform.policy.SubjectSet; +import io.opentdf.platform.policy.ConditionGroup; +import io.opentdf.platform.policy.Condition; +import io.opentdf.platform.policy.ConditionBooleanTypeEnum; +import io.opentdf.platform.policy.SubjectMappingOperatorEnum; + + +public class CreateSubjectConditionSet { + public static void main(String[] args) throws ExecutionException, InterruptedException{ + + String clientId = "opentdf"; + String clientSecret = "secret"; + String platformEndpoint = "localhost:8080"; + + SDKBuilder builder = new SDKBuilder(); + SDK sdk = builder.platformEndpoint(platformEndpoint) + .clientSecret(clientId, clientSecret).useInsecurePlaintextConnection(true) + .build(); + + var subjectset = SubjectSet.newBuilder().addConditionGroups(ConditionGroup.newBuilder() + .setBooleanOperator(ConditionBooleanTypeEnum.CONDITION_BOOLEAN_TYPE_ENUM_AND) + .addConditions(Condition.newBuilder() + .setSubjectExternalSelectorValue(".myfield") + .setOperator(SubjectMappingOperatorEnum.SUBJECT_MAPPING_OPERATOR_ENUM_IN) + .addSubjectExternalValues("myvalue") + )); + + CreateSubjectConditionSetRequest request = CreateSubjectConditionSetRequest.newBuilder() + .setSubjectConditionSet( + SubjectConditionSetCreate.newBuilder().addSubjectSets(subjectset)) + .build(); + + CreateSubjectConditionSetResponse resp = sdk.getServices().subjectMappings().createSubjectConditionSet(request).get(); + + SubjectConditionSet scs = resp.getSubjectConditionSet(); + + System.out.println(scs.getId()); + } +} diff --git a/examples/src/main/java/io/opentdf/platform/CreateSubjectMapping.java b/examples/src/main/java/io/opentdf/platform/CreateSubjectMapping.java new file mode 100644 index 00000000..41f07336 --- /dev/null +++ b/examples/src/main/java/io/opentdf/platform/CreateSubjectMapping.java @@ -0,0 +1,34 @@ +package io.opentdf.platform; +import io.opentdf.platform.sdk.*; + +import java.util.concurrent.ExecutionException; + +import io.opentdf.platform.policy.subjectmapping.*; +import io.opentdf.platform.policy.SubjectMapping; +import io.opentdf.platform.policy.Action; + +public class CreateSubjectMapping { + public static void main(String[] args) throws ExecutionException, InterruptedException{ + + String clientId = "opentdf"; + String clientSecret = "secret"; + String platformEndpoint = "localhost:8080"; + + SDKBuilder builder = new SDKBuilder(); + SDK sdk = builder.platformEndpoint(platformEndpoint) + .clientSecret(clientId, clientSecret).useInsecurePlaintextConnection(true) + .build(); + + CreateSubjectMappingRequest request = CreateSubjectMappingRequest.newBuilder() + .setAttributeValueId("33c47777-f3b6-492d-bcd2-5329e0aab642") + .addActions(Action.newBuilder().setStandard(Action.StandardAction.STANDARD_ACTION_DECRYPT)) + .setExistingSubjectConditionSetId("9009fde8-d22b-4dfb-a456-f9ce6943244a") + .build(); + + CreateSubjectMappingResponse resp = sdk.getServices().subjectMappings().createSubjectMapping(request).get(); + + SubjectMapping sm = resp.getSubjectMapping(); + + System.out.println(sm.getId()); + } +} diff --git a/examples/src/main/java/io/opentdf/platform/DecryptExample.java b/examples/src/main/java/io/opentdf/platform/DecryptExample.java new file mode 100644 index 00000000..693192ca --- /dev/null +++ b/examples/src/main/java/io/opentdf/platform/DecryptExample.java @@ -0,0 +1,42 @@ +package io.opentdf.platform; +import io.opentdf.platform.sdk.*; +import java.nio.file.StandardOpenOption; +import java.nio.channels.FileChannel; +import java.nio.file.Path; +import java.nio.file.Paths; + +import com.nimbusds.jose.JOSEException; +import java.io.IOException; +import java.util.concurrent.ExecutionException; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.text.ParseException; +import javax.crypto.BadPaddingException; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import org.apache.commons.codec.DecoderException; + + +public class DecryptExample { + public static void main(String[] args) throws IOException, + InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, + BadPaddingException, InvalidKeyException, TDF.FailedToCreateGMAC, + JOSEException, ParseException, NoSuchAlgorithmException, DecoderException { + + String clientId = "opentdf"; + String clientSecret = "secret"; + String platformEndpoint = "localhost:8080"; + + SDKBuilder builder = new SDKBuilder(); + SDK sdk = builder.platformEndpoint(platformEndpoint) + .clientSecret(clientId, clientSecret).useInsecurePlaintextConnection(true) + .build(); + + Path path = Paths.get("my.ciphertext"); + try (var in = FileChannel.open(path, StandardOpenOption.READ)) { + var reader = new TDF().loadTDF(in, sdk.getServices().kas()); + reader.readPayload(System.out); + } + } +} diff --git a/examples/src/main/java/io/opentdf/platform/EncryptExample.java b/examples/src/main/java/io/opentdf/platform/EncryptExample.java new file mode 100644 index 00000000..6da5d47c --- /dev/null +++ b/examples/src/main/java/io/opentdf/platform/EncryptExample.java @@ -0,0 +1,39 @@ +package io.opentdf.platform; +import io.opentdf.platform.sdk.*; +import java.io.ByteArrayInputStream; +import java.io.BufferedOutputStream; +import java.nio.charset.StandardCharsets; +import java.io.FileOutputStream; + +import com.nimbusds.jose.JOSEException; +import java.io.IOException; +import java.util.concurrent.ExecutionException; + +public class EncryptExample { + public static void main(String[] args) throws IOException, JOSEException, AutoConfigureException, InterruptedException, ExecutionException { + String clientId = "opentdf"; + String clientSecret = "secret"; + String platformEndpoint = "localhost:8080"; + + SDKBuilder builder = new SDKBuilder(); + SDK sdk = builder.platformEndpoint(platformEndpoint) + .clientSecret(clientId, clientSecret).useInsecurePlaintextConnection(true) + .build(); + + var kasInfo = new Config.KASInfo(); + kasInfo.URL = "http://localhost:8080/kas"; + + var tdfConfig = Config.newTDFConfig(Config.withKasInformation(kasInfo), Config.withDataAttributes("https://example.com/attr/color/value/red")); + + String str = "Hello, World!"; + + // Convert String to InputStream + var in = new ByteArrayInputStream(str.getBytes(StandardCharsets.UTF_8)); + + FileOutputStream fos = new FileOutputStream("my.ciphertext"); + + new TDF().createTDF(in, fos, tdfConfig, + sdk.getServices().kas(), + sdk.getServices().attributes()); + } +} diff --git a/examples/src/main/java/io/opentdf/platform/GetDecisions.java b/examples/src/main/java/io/opentdf/platform/GetDecisions.java new file mode 100644 index 00000000..4bac7694 --- /dev/null +++ b/examples/src/main/java/io/opentdf/platform/GetDecisions.java @@ -0,0 +1,37 @@ +package io.opentdf.platform; +import io.opentdf.platform.sdk.*; + +import java.util.concurrent.ExecutionException; + +import io.opentdf.platform.authorization.*; +import io.opentdf.platform.policy.Action; + +import java.util.List; + +public class GetDecisions { + public static void main(String[] args) throws ExecutionException, InterruptedException{ + + String clientId = "opentdf"; + String clientSecret = "secret"; + String platformEndpoint = "localhost:8080"; + + SDKBuilder builder = new SDKBuilder(); + SDK sdk = builder.platformEndpoint(platformEndpoint) + .clientSecret(clientId, clientSecret).useInsecurePlaintextConnection(true) + .build(); + + GetDecisionsRequest request = GetDecisionsRequest.newBuilder() + .addDecisionRequests(DecisionRequest.newBuilder() + .addEntityChains(EntityChain.newBuilder().setId("ec1").addEntities(Entity.newBuilder().setId("entity-1").setClientId("opentdf"))) + .addActions(Action.newBuilder().setStandard(Action.StandardAction.STANDARD_ACTION_DECRYPT)) + .addResourceAttributes(ResourceAttribute.newBuilder().setResourceAttributesId("resource-attribute-1") + .addAttributeValueFqns("https://mynamespace.com/attr/test/value/test1")) + ).build(); + + GetDecisionsResponse resp = sdk.getServices().authorization().getDecisions(request).get(); + + List decisions = resp.getDecisionResponsesList(); + + System.out.println(DecisionResponse.Decision.forNumber(decisions.get(0).getDecisionValue())); + } +} diff --git a/examples/src/main/java/io/opentdf/platform/GetEntitlements.java b/examples/src/main/java/io/opentdf/platform/GetEntitlements.java new file mode 100644 index 00000000..ff484b80 --- /dev/null +++ b/examples/src/main/java/io/opentdf/platform/GetEntitlements.java @@ -0,0 +1,32 @@ +package io.opentdf.platform; +import io.opentdf.platform.sdk.*; + +import java.util.concurrent.ExecutionException; + +import io.opentdf.platform.authorization.*; + +import java.util.List; + +public class GetEntitlements { + public static void main(String[] args) throws ExecutionException, InterruptedException{ + + String clientId = "opentdf"; + String clientSecret = "secret"; + String platformEndpoint = "localhost:8080"; + + SDKBuilder builder = new SDKBuilder(); + SDK sdk = builder.platformEndpoint(platformEndpoint) + .clientSecret(clientId, clientSecret).useInsecurePlaintextConnection(true) + .build(); + + GetEntitlementsRequest request = GetEntitlementsRequest.newBuilder() + .addEntities(Entity.newBuilder().setId("entity-1").setClientId("opentdf")) + .build(); + + GetEntitlementsResponse resp = sdk.getServices().authorization().getEntitlements(request).get(); + + List entitlements = resp.getEntitlementsList(); + + System.out.println(entitlements.get(0).getAttributeValueFqnsList()); + } +} diff --git a/examples/src/main/java/io/opentdf/platform/ListAttributes.java b/examples/src/main/java/io/opentdf/platform/ListAttributes.java new file mode 100644 index 00000000..366af20a --- /dev/null +++ b/examples/src/main/java/io/opentdf/platform/ListAttributes.java @@ -0,0 +1,34 @@ +package io.opentdf.platform; +import io.opentdf.platform.sdk.*; + +import java.util.concurrent.ExecutionException; + +import io.opentdf.platform.policy.AttributeRuleTypeEnum; + +import io.opentdf.platform.policy.attributes.*; +import io.opentdf.platform.policy.Attribute; + +import java.util.List; + +public class ListAttributes { + public static void main(String[] args) throws ExecutionException, InterruptedException{ + + String clientId = "opentdf"; + String clientSecret = "secret"; + String platformEndpoint = "localhost:8080"; + + SDKBuilder builder = new SDKBuilder(); + SDK sdk = builder.platformEndpoint(platformEndpoint) + .clientSecret(clientId, clientSecret).useInsecurePlaintextConnection(true) + .build(); + + ListAttributesRequest request = ListAttributesRequest.newBuilder() + .setNamespace("mynamespace.com").build(); + + ListAttributesResponse resp = sdk.getServices().attributes().listAttributes(request).get(); + + List attributes = resp.getAttributesList(); + + System.out.println(resp.getAttributesCount()); + } +} diff --git a/examples/src/main/java/io/opentdf/platform/ListNamespaces.java b/examples/src/main/java/io/opentdf/platform/ListNamespaces.java new file mode 100644 index 00000000..43e514a5 --- /dev/null +++ b/examples/src/main/java/io/opentdf/platform/ListNamespaces.java @@ -0,0 +1,27 @@ +package io.opentdf.platform; +import io.opentdf.platform.sdk.*; + +import java.util.concurrent.ExecutionException; + +import io.opentdf.platform.policy.namespaces.*; +import io.opentdf.platform.policy.Namespace; + +public class ListNamespaces { + public static void main(String[] args) throws ExecutionException, InterruptedException{ + + String clientId = "opentdf"; + String clientSecret = "secret"; + String platformEndpoint = "localhost:8080"; + + SDKBuilder builder = new SDKBuilder(); + SDK sdk = builder.platformEndpoint(platformEndpoint) + .clientSecret(clientId, clientSecret).useInsecurePlaintextConnection(true) + .build(); + + ListNamespacesRequest request = ListNamespacesRequest.newBuilder().build(); + + ListNamespacesResponse resp = sdk.getServices().namespaces().listNamespaces(request).get(); + + java.util.List namespaces = resp.getNamespacesList(); + } +} diff --git a/examples/src/main/java/io/opentdf/platform/ListSubjectMappings.java b/examples/src/main/java/io/opentdf/platform/ListSubjectMappings.java new file mode 100644 index 00000000..12bc4ab7 --- /dev/null +++ b/examples/src/main/java/io/opentdf/platform/ListSubjectMappings.java @@ -0,0 +1,32 @@ +package io.opentdf.platform; +import io.opentdf.platform.sdk.*; + +import java.util.concurrent.ExecutionException; + +import io.opentdf.platform.policy.subjectmapping.*; +import io.opentdf.platform.policy.SubjectMapping; + +import java.util.List; + +public class ListSubjectMappings { + public static void main(String[] args) throws ExecutionException, InterruptedException{ + + String clientId = "opentdf"; + String clientSecret = "secret"; + String platformEndpoint = "localhost:8080"; + + SDKBuilder builder = new SDKBuilder(); + SDK sdk = builder.platformEndpoint(platformEndpoint) + .clientSecret(clientId, clientSecret).useInsecurePlaintextConnection(true) + .build(); + + ListSubjectMappingsRequest request = ListSubjectMappingsRequest.newBuilder().build(); + + ListSubjectMappingsResponse resp = sdk.getServices().subjectMappings().listSubjectMappings(request).get(); + + List sms = resp.getSubjectMappingsList(); + + System.out.println(sms.size()); + System.out.println(sms.get(0).getId()); + } +} diff --git a/examples/src/main/resources/log4j2.xml b/examples/src/main/resources/log4j2.xml new file mode 100644 index 00000000..da185a60 --- /dev/null +++ b/examples/src/main/resources/log4j2.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/pom.xml b/pom.xml index 30fe54e6..e1b0ec0f 100644 --- a/pom.xml +++ b/pom.xml @@ -267,6 +267,7 @@ sdk cmdline + examples true From 78d7b66e40bb52340e604ab645830287c91ba534 Mon Sep 17 00:00:00 2001 From: sujankota Date: Tue, 29 Oct 2024 14:11:12 -0400 Subject: [PATCH 08/14] fix(sdk): option to disable assertion verification (#205) --- .../java/io/opentdf/platform/sdk/Config.java | 8 ++- .../java/io/opentdf/platform/sdk/TDF.java | 18 ++++- .../io/opentdf/platform/sdk/TDFE2ETest.java | 2 +- .../java/io/opentdf/platform/sdk/TDFTest.java | 67 ++++++++++++++++++- 4 files changed, 87 insertions(+), 8 deletions(-) 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 c7d4f092..1f44854a 100644 --- a/sdk/src/main/java/io/opentdf/platform/sdk/Config.java +++ b/sdk/src/main/java/io/opentdf/platform/sdk/Config.java @@ -91,12 +91,14 @@ AssertionConfig.AssertionKey getKey(String key) { public static class TDFReaderConfig { // Optional Map of Assertion Verification Keys - AssertionVerificationKeys assertionVerificationKeys; + AssertionVerificationKeys assertionVerificationKeys = new AssertionVerificationKeys(); + boolean disableAssertionVerification; } @SafeVarargs public static TDFReaderConfig newTDFReaderConfig(Consumer... options) { TDFReaderConfig config = new TDFReaderConfig(); + config.disableAssertionVerification = false; for (Consumer option : options) { option.accept(config); } @@ -108,6 +110,10 @@ public static Consumer withAssertionVerificationKeys( return (TDFReaderConfig config) -> config.assertionVerificationKeys = assertionVerificationKeys; } + public static Consumer withDisableAssertionVerification(boolean disable) { + return (TDFReaderConfig config) -> config.disableAssertionVerification = disable; + } + public static class TDFConfig { public Boolean autoconfigure; public int defaultSegmentSize; 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 aec26e80..d694cea7 100644 --- a/sdk/src/main/java/io/opentdf/platform/sdk/TDF.java +++ b/sdk/src/main/java/io/opentdf/platform/sdk/TDF.java @@ -550,8 +550,14 @@ 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, new Config.TDFReaderConfig()); + } + + public Reader loadTDF(SeekableByteChannel tdf, SDK.KAS kas, - Config.AssertionVerificationKeys... assertionVerificationKeys) + Config.TDFReaderConfig tdfReaderConfig) throws NotValidateRootSignature, SegmentSizeMismatch, IOException, FailedToCreateGMAC, JOSEException, ParseException, NoSuchAlgorithmException, DecoderException { @@ -672,10 +678,16 @@ public Reader loadTDF(SeekableByteChannel tdf, SDK.KAS kas, // Validate assertions for (var assertion : manifest.assertions) { + // Skip assertion verification if disabled + if (tdfReaderConfig.disableAssertionVerification) { + break; + } + // Set default to HS256 var assertionKey = new AssertionConfig.AssertionKey(AssertionConfig.AssertionKeyAlg.HS256, payloadKey); - if (assertionVerificationKeys != null && assertionVerificationKeys.length > 0) { - var keyForAssertion = assertionVerificationKeys[0].getKey(assertion.id); + Config.AssertionVerificationKeys assertionVerificationKeys = tdfReaderConfig.assertionVerificationKeys; + if (!assertionVerificationKeys.isEmpty()) { + var keyForAssertion = assertionVerificationKeys.getKey(assertion.id); if (keyForAssertion != null) { assertionKey = keyForAssertion; } 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 fb44663c..11a41bc8 100644 --- a/sdk/src/test/java/io/opentdf/platform/sdk/TDFE2ETest.java +++ b/sdk/src/test/java/io/opentdf/platform/sdk/TDFE2ETest.java @@ -38,7 +38,7 @@ public void createAndDecryptTdfIT() throws Exception { 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()); + var reader = tdf.loadTDF(new SeekableInMemoryByteChannel(tdfOutputStream.toByteArray()), sdk.kas()); reader.readPayload(unwrappedData); assertThat(unwrappedData.toString(StandardCharsets.UTF_8)).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 e4fed930..8afb5545 100644 --- a/sdk/src/test/java/io/opentdf/platform/sdk/TDFTest.java +++ b/sdk/src/test/java/io/opentdf/platform/sdk/TDFTest.java @@ -2,6 +2,7 @@ import com.google.common.util.concurrent.ListenableFuture; +import com.nimbusds.jose.JOSEException; import io.opentdf.platform.policy.attributes.GetAttributeValuesByFqnsRequest; import io.opentdf.platform.policy.attributes.GetAttributeValuesByFqnsResponse; import io.opentdf.platform.policy.attributes.AttributesServiceGrpc; @@ -136,8 +137,10 @@ void testSimpleTDFEncryptAndDecrypt() throws Exception { key); var unwrappedData = new ByteArrayOutputStream(); + Config.TDFReaderConfig readerConfig = Config.newTDFReaderConfig( + Config.withAssertionVerificationKeys(assertionVerificationKeys)); var reader = tdf.loadTDF(new SeekableInMemoryByteChannel(tdfOutputStream.toByteArray()), kas, - assertionVerificationKeys); + readerConfig); assertThat(reader.getManifest().payload.mimeType).isEqualTo("application/octet-stream"); reader.readPayload(unwrappedData); @@ -192,8 +195,10 @@ void testSimpleTDFWithAssertionWithRS256() throws Exception { new AssertionConfig.AssertionKey(AssertionConfig.AssertionKeyAlg.RS256, keypair.getPublic())); var unwrappedData = new ByteArrayOutputStream(); + Config.TDFReaderConfig readerConfig = Config.newTDFReaderConfig( + Config.withAssertionVerificationKeys(assertionVerificationKeys)); var reader = tdf.loadTDF(new SeekableInMemoryByteChannel(tdfOutputStream.toByteArray()), kas, - assertionVerificationKeys); + readerConfig); reader.readPayload(unwrappedData); assertThat(unwrappedData.toString(StandardCharsets.UTF_8)) @@ -201,6 +206,61 @@ void testSimpleTDFWithAssertionWithRS256() throws Exception { .isEqualTo(plainText); } + @Test + void testWithAssertionVerificationDisabled() throws Exception { + + ListenableFuture resp1 = mock(ListenableFuture.class); + lenient().when(resp1.get()).thenReturn(GetAttributeValuesByFqnsResponse.newBuilder().build()); + lenient().when(attributeGrpcStub.getAttributeValuesByFqns(any(GetAttributeValuesByFqnsRequest.class))) + .thenReturn(resp1); + + String assertion1Id = "assertion1"; + var keypair = CryptoUtils.generateRSAKeypair(); + var assertionConfig = new AssertionConfig(); + assertionConfig.id = assertion1Id; + assertionConfig.type = AssertionConfig.Type.BaseAssertion; + assertionConfig.scope = AssertionConfig.Scope.TrustedDataObj; + assertionConfig.appliesToState = AssertionConfig.AppliesToState.Unencrypted; + assertionConfig.statement = new AssertionConfig.Statement(); + assertionConfig.statement.format = "base64binary"; + assertionConfig.statement.schema = "text"; + assertionConfig.statement.value = "ICAgIDxlZGoOkVkaD4="; + assertionConfig.assertionKey = new AssertionConfig.AssertionKey(AssertionConfig.AssertionKeyAlg.RS256, + keypair.getPrivate()); + + Config.TDFConfig config = Config.newTDFConfig( + Config.withAutoconfigure(false), + Config.withKasInformation(getKASInfos()), + Config.withAssertionConfig(assertionConfig)); + + String plainText = "this is extremely sensitive stuff!!!"; + InputStream plainTextInputStream = new ByteArrayInputStream(plainText.getBytes()); + ByteArrayOutputStream tdfOutputStream = new ByteArrayOutputStream(); + + TDF tdf = new TDF(); + tdf.createTDF(plainTextInputStream, tdfOutputStream, config, kas, attributeGrpcStub); + + 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, + new Config.TDFReaderConfig()); + }); + + // 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); + reader.readPayload(unwrappedData); + + assertThat(unwrappedData.toString(StandardCharsets.UTF_8)) + .withFailMessage("extracted data does not match") + .isEqualTo(plainText); + } @Test void testSimpleTDFWithAssertionWithHS256() throws Exception { @@ -244,7 +304,8 @@ void testSimpleTDFWithAssertionWithHS256() throws Exception { tdf.createTDF(plainTextInputStream, tdfOutputStream, config, kas, attributeGrpcStub); var unwrappedData = new ByteArrayOutputStream(); - var reader = tdf.loadTDF(new SeekableInMemoryByteChannel(tdfOutputStream.toByteArray()), kas); + var reader = tdf.loadTDF(new SeekableInMemoryByteChannel(tdfOutputStream.toByteArray()), + kas, new Config.TDFReaderConfig()); reader.readPayload(unwrappedData); assertThat(unwrappedData.toString(StandardCharsets.UTF_8)) From 088b88e48a63066880e840c9104df84ec0cb22c9 Mon Sep 17 00:00:00 2001 From: "opentdf-automation[bot]" <149537512+opentdf-automation[bot]@users.noreply.github.com> Date: Tue, 29 Oct 2024 15:12:44 -0400 Subject: [PATCH 09/14] chore(main): release 0.7.5-SNAPSHOT (#203) :robot: I have created a release *beep* *boop* ---
0.7.5-SNAPSHOT ### Updating meta-information for bleeding-edge SNAPSHOT release.
--- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). --------- Co-authored-by: opentdf-automation[bot] <149537512+opentdf-automation[bot]@users.noreply.github.com> Co-authored-by: Morgan Kleene --- cmdline/pom.xml | 2 +- examples/pom.xml | 4 ++-- pom.xml | 2 +- sdk/pom.xml | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/cmdline/pom.xml b/cmdline/pom.xml index 897f5658..55916094 100644 --- a/cmdline/pom.xml +++ b/cmdline/pom.xml @@ -4,7 +4,7 @@ io.opentdf.platform sdk-pom - 0.7.4 + 0.7.5-SNAPSHOT cmdline diff --git a/examples/pom.xml b/examples/pom.xml index 28915c44..922f6b8b 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -4,12 +4,12 @@ sdk-pom io.opentdf.platform - 0.7.4 + 0.7.5-SNAPSHOT io.opentdf.platform examples - 0.7.4 + 0.7.5-SNAPSHOT examples diff --git a/pom.xml b/pom.xml index e1b0ec0f..d37b49cf 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ io.opentdf.platform sdk-pom - 0.7.4 + 0.7.5-SNAPSHOT io.opentdf.platform:sdk-pom OpenTDF Java SDK https://github.com/opentdf/java-sdk diff --git a/sdk/pom.xml b/sdk/pom.xml index db01dbc2..5dfe5539 100644 --- a/sdk/pom.xml +++ b/sdk/pom.xml @@ -6,7 +6,7 @@ sdk-pom io.opentdf.platform - 0.7.4 + 0.7.5-SNAPSHOT jar From 94b161d5330fbbf32923a4065c5dfa77a6a06f1b Mon Sep 17 00:00:00 2001 From: "opentdf-automation[bot]" <149537512+opentdf-automation[bot]@users.noreply.github.com> Date: Tue, 29 Oct 2024 15:45:14 -0400 Subject: [PATCH 10/14] chore(main): release 0.7.5 (#206) :robot: I have created a release *beep* *boop* ---
0.7.5 ## [0.7.5](https://github.com/opentdf/java-sdk/compare/v0.7.4...v0.7.5) (2024-10-29) ### Features * Examples module ([#202](https://github.com/opentdf/java-sdk/issues/202)) ([ac13a0a](https://github.com/opentdf/java-sdk/commit/ac13a0a7c82caed920238244cf7adaca3039fdea)) ### Bug Fixes * **sdk:** option to disable assertion verification ([#205](https://github.com/opentdf/java-sdk/issues/205)) ([78d7b66](https://github.com/opentdf/java-sdk/commit/78d7b66e40bb52340e604ab645830287c91ba534))
--- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). --------- Co-authored-by: opentdf-automation[bot] <149537512+opentdf-automation[bot]@users.noreply.github.com> Co-authored-by: Morgan Kleene --- .release-please-manifest.json | 2 +- CHANGELOG.md | 12 ++++++++++++ cmdline/pom.xml | 2 +- examples/pom.xml | 3 +-- pom.xml | 2 +- sdk/pom.xml | 2 +- 6 files changed, 17 insertions(+), 6 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 904b037e..5a8dc58d 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.7.4" + ".": "0.7.5" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 453b346e..59e03bdf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,17 @@ # Changelog +## [0.7.5](https://github.com/opentdf/java-sdk/compare/v0.7.4...v0.7.5) (2024-10-29) + + +### Features + +* Examples module ([#202](https://github.com/opentdf/java-sdk/issues/202)) ([ac13a0a](https://github.com/opentdf/java-sdk/commit/ac13a0a7c82caed920238244cf7adaca3039fdea)) + + +### Bug Fixes + +* **sdk:** option to disable assertion verification ([#205](https://github.com/opentdf/java-sdk/issues/205)) ([78d7b66](https://github.com/opentdf/java-sdk/commit/78d7b66e40bb52340e604ab645830287c91ba534)) + ## [0.7.4](https://github.com/opentdf/java-sdk/compare/v0.7.3...v0.7.4) (2024-10-24) diff --git a/cmdline/pom.xml b/cmdline/pom.xml index 55916094..bc33e5cc 100644 --- a/cmdline/pom.xml +++ b/cmdline/pom.xml @@ -4,7 +4,7 @@ io.opentdf.platform sdk-pom - 0.7.5-SNAPSHOT + 0.7.5 cmdline diff --git a/examples/pom.xml b/examples/pom.xml index 922f6b8b..47bb4783 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -4,12 +4,11 @@ sdk-pom io.opentdf.platform - 0.7.5-SNAPSHOT + 0.7.5 io.opentdf.platform examples - 0.7.5-SNAPSHOT examples diff --git a/pom.xml b/pom.xml index d37b49cf..826a7dcc 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ io.opentdf.platform sdk-pom - 0.7.5-SNAPSHOT + 0.7.5 io.opentdf.platform:sdk-pom OpenTDF Java SDK https://github.com/opentdf/java-sdk diff --git a/sdk/pom.xml b/sdk/pom.xml index 5dfe5539..678c78b0 100644 --- a/sdk/pom.xml +++ b/sdk/pom.xml @@ -6,7 +6,7 @@ sdk-pom io.opentdf.platform - 0.7.5-SNAPSHOT + 0.7.5 jar From b4f95e6756f1f60fdfc8b0c2addc4e51aca4352b Mon Sep 17 00:00:00 2001 From: Elizabeth Healy <35498075+elizabethhealy@users.noreply.github.com> Date: Wed, 30 Oct 2024 13:39:50 -0400 Subject: [PATCH 11/14] feat(sdk): Add and expose tamper error types (#187) Exception type for tamper detection -- make it the parent to the exception types we want to be able to catch for tamper --------- Co-authored-by: Dave Mihalcik --- .../io/opentdf/platform/sdk/KASClient.java | 20 ++++++- .../io/opentdf/platform/sdk/Manifest.java | 5 +- .../java/io/opentdf/platform/sdk/TDF.java | 38 ++++++++---- .../java/io/opentdf/platform/sdk/TDFTest.java | 58 ++++++++++++++++++- 4 files changed, 106 insertions(+), 15 deletions(-) diff --git a/sdk/src/main/java/io/opentdf/platform/sdk/KASClient.java b/sdk/src/main/java/io/opentdf/platform/sdk/KASClient.java index 727eda83..806d48d0 100644 --- a/sdk/src/main/java/io/opentdf/platform/sdk/KASClient.java +++ b/sdk/src/main/java/io/opentdf/platform/sdk/KASClient.java @@ -9,13 +9,17 @@ import com.nimbusds.jwt.JWTClaimsSet; import com.nimbusds.jwt.SignedJWT; import io.grpc.ManagedChannel; +import io.grpc.StatusRuntimeException; +import io.grpc.Status; import io.opentdf.platform.kas.AccessServiceGrpc; import io.opentdf.platform.kas.PublicKeyRequest; import io.opentdf.platform.kas.PublicKeyResponse; import io.opentdf.platform.kas.RewrapRequest; +import io.opentdf.platform.kas.RewrapResponse; import io.opentdf.platform.sdk.Config.KASInfo; import io.opentdf.platform.sdk.nanotdf.ECKeyPair; import io.opentdf.platform.sdk.nanotdf.NanoTDFType; +import io.opentdf.platform.sdk.TDF.KasBadRequestException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; @@ -182,9 +186,19 @@ public byte[] unwrap(Manifest.KeyAccess keyAccess, String policy) { .newBuilder() .setSignedRequestToken(jwt.serialize()) .build(); - var response = getStub(keyAccess.url).rewrap(request); - var wrappedKey = response.getEntityWrappedKey().toByteArray(); - return decryptor.decrypt(wrappedKey); + RewrapResponse response; + try { + response = getStub(keyAccess.url).rewrap(request); + var wrappedKey = response.getEntityWrappedKey().toByteArray(); + return decryptor.decrypt(wrappedKey); + } catch (StatusRuntimeException e) { + if (e.getStatus().getCode() == Status.Code.INVALID_ARGUMENT) { + // 400 Bad Request + throw new KasBadRequestException("rewrap request 400: " + e.toString()); + } + throw e; + } + } public byte[] unwrapNanoTDF(NanoTDFType.ECCurve curve, String header, String kasURL) { diff --git a/sdk/src/main/java/io/opentdf/platform/sdk/Manifest.java b/sdk/src/main/java/io/opentdf/platform/sdk/Manifest.java index bc4c4c3c..aca6d1cd 100644 --- a/sdk/src/main/java/io/opentdf/platform/sdk/Manifest.java +++ b/sdk/src/main/java/io/opentdf/platform/sdk/Manifest.java @@ -10,6 +10,9 @@ import com.nimbusds.jose.crypto.RSASSAVerifier; import com.nimbusds.jwt.JWTClaimsSet; import com.nimbusds.jwt.SignedJWT; + +import io.opentdf.platform.sdk.TDF.AssertionException; + import org.apache.commons.codec.binary.Hex; import org.erdtman.jcs.JsonCanonicalizer; @@ -381,7 +384,7 @@ public void sign(final HashValues hashValues, final AssertionConfig.AssertionKey public Assertion.HashValues verify(AssertionConfig.AssertionKey assertionKey) throws ParseException, JOSEException { if (binding == null) { - throw new SDKException("Binding is null in assertion"); + throw new AssertionException("Binding is null in assertion", this.id); } String signatureString = binding.signature; 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 d694cea7..4c6eb6da 100644 --- a/sdk/src/main/java/io/opentdf/platform/sdk/TDF.java +++ b/sdk/src/main/java/io/opentdf/platform/sdk/TDF.java @@ -119,30 +119,48 @@ public FailedToCreateGMAC(String errorMessage) { } } - public static class NotValidateRootSignature extends RuntimeException { - public NotValidateRootSignature(String errorMessage) { + public static class TDFReadFailed extends RuntimeException { + public TDFReadFailed(String errorMessage) { + super(errorMessage); + } + } + + public static class TamperException extends SDKException { + public TamperException(String errorMessage) { + super("[tamper detected] "+errorMessage); + } + } + + public static class RootSignatureValidationException extends TamperException { + public RootSignatureValidationException(String errorMessage) { super(errorMessage); } } - public static class SegmentSizeMismatch extends RuntimeException { + public static class SegmentSizeMismatch extends TamperException { public SegmentSizeMismatch(String errorMessage) { super(errorMessage); } } - public static class SegmentSignatureMismatch extends RuntimeException { + public static class SegmentSignatureMismatch extends TamperException { public SegmentSignatureMismatch(String errorMessage) { super(errorMessage); } } - public static class TDFReadFailed extends RuntimeException { - public TDFReadFailed(String errorMessage) { + public static class KasBadRequestException extends TamperException { + public KasBadRequestException(String errorMessage) { super(errorMessage); } } + public static class AssertionException extends TamperException { + public AssertionException(String errorMessage, String id) { + super("assertion id: "+ id + "; " + errorMessage); + } + } + public static class EncryptedMetadata { private String ciphertext; private String iv; @@ -558,7 +576,7 @@ public Reader loadTDF(SeekableByteChannel tdf, SDK.KAS kas) public Reader loadTDF(SeekableByteChannel tdf, SDK.KAS kas, Config.TDFReaderConfig tdfReaderConfig) - throws NotValidateRootSignature, SegmentSizeMismatch, + throws RootSignatureValidationException, SegmentSizeMismatch, IOException, FailedToCreateGMAC, JOSEException, ParseException, NoSuchAlgorithmException, DecoderException { TDFReader tdfReader = new TDFReader(tdf); @@ -666,7 +684,7 @@ public Reader loadTDF(SeekableByteChannel tdf, SDK.KAS kas, } if (rootSignature.compareTo(rootSigValue) != 0) { - throw new NotValidateRootSignature("root signature validation failed"); + throw new RootSignatureValidationException("root signature validation failed"); } int segmentSize = manifest.encryptionInformation.integrityInformation.segmentSizeDefault; @@ -701,11 +719,11 @@ public Reader loadTDF(SeekableByteChannel tdf, SDK.KAS kas, var encodeSignature = Base64.getEncoder().encodeToString(signature.getBytes()); if (!Objects.equals(hashOfAssertion, hashValues.getAssertionHash())) { - throw new SDKException("assertion hash mismatch"); + throw new AssertionException("assertion hash mismatch", assertion.id); } if (!Objects.equals(encodeSignature, hashValues.getSignature())) { - throw new SDKException("failed integrity check on assertion signature"); + throw new AssertionException("failed integrity check on assertion signature", assertion.id); } } 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 8afb5545..acd1fd15 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,7 @@ import io.opentdf.platform.policy.attributes.GetAttributeValuesByFqnsResponse; import io.opentdf.platform.policy.attributes.AttributesServiceGrpc; import io.opentdf.platform.sdk.Config.KASInfo; +import io.opentdf.platform.sdk.TDF.Reader; import io.opentdf.platform.sdk.nanotdf.NanoTDFType; import org.apache.commons.compress.utils.SeekableInMemoryByteChannel; import org.junit.jupiter.api.BeforeAll; @@ -30,7 +31,6 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.lenient; import static org.mockito.Mockito.mock; - import static org.assertj.core.api.AssertionsForClassTypes.assertThat; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -333,6 +333,62 @@ void testSimpleTDFWithAssertionWithHS256() throws Exception { } } + @Test + void testSimpleTDFWithAssertionWithHS256Failure() throws Exception { + + ListenableFuture resp1 = mock(ListenableFuture.class); + lenient().when(resp1.get()).thenReturn(GetAttributeValuesByFqnsResponse.newBuilder().build()); + lenient().when(attributeGrpcStub.getAttributeValuesByFqns(any(GetAttributeValuesByFqnsRequest.class))) + .thenReturn(resp1); + + // var keypair = CryptoUtils.generateRSAKeypair(); + SecureRandom secureRandom = new SecureRandom(); + byte[] key = new byte[32]; + secureRandom.nextBytes(key); + + String assertion1Id = "assertion1"; + var assertionConfig1 = new AssertionConfig(); + assertionConfig1.id = assertion1Id; + assertionConfig1.type = AssertionConfig.Type.BaseAssertion; + assertionConfig1.scope = AssertionConfig.Scope.TrustedDataObj; + assertionConfig1.appliesToState = AssertionConfig.AppliesToState.Unencrypted; + assertionConfig1.statement = new AssertionConfig.Statement(); + assertionConfig1.statement.format = "base64binary"; + assertionConfig1.statement.schema = "text"; + assertionConfig1.statement.value = "ICAgIDxlZGoOkVkaD4="; + assertionConfig1.assertionKey = new AssertionConfig.AssertionKey(AssertionConfig.AssertionKeyAlg.HS256, key); + + Config.TDFConfig config = Config.newTDFConfig( + Config.withAutoconfigure(false), + Config.withKasInformation(getKASInfos()), + Config.withAssertionConfig(assertionConfig1)); + + String plainText = "this is extremely sensitive stuff!!!"; + InputStream plainTextInputStream = new ByteArrayInputStream(plainText.getBytes()); + ByteArrayOutputStream tdfOutputStream = new ByteArrayOutputStream(); + + TDF tdf = new TDF(); + tdf.createTDF(plainTextInputStream, tdfOutputStream, config, kas, attributeGrpcStub); + + byte[] notkey = new byte[32]; + secureRandom.nextBytes(notkey); + var assertionVerificationKeys = new Config.AssertionVerificationKeys(); + assertionVerificationKeys.defaultKey = new AssertionConfig.AssertionKey(AssertionConfig.AssertionKeyAlg.HS256, + notkey); + Config.TDFReaderConfig readerConfig = Config.newTDFReaderConfig( + Config.withAssertionVerificationKeys(assertionVerificationKeys)); + + var unwrappedData = new ByteArrayOutputStream(); + Reader reader; + try { + reader = tdf.loadTDF(new SeekableInMemoryByteChannel(tdfOutputStream.toByteArray()), kas, readerConfig); + throw new RuntimeException("assertion verify key error thrown"); + + } catch (SDKException e) { + assertThat(e).hasMessageContaining("verify"); + } + } + @Test public void testCreatingTDFWithMultipleSegments() throws Exception { From 6301d32c17b31c710073d898edcc2fb4ff1d3e36 Mon Sep 17 00:00:00 2001 From: Mike Jensen Date: Tue, 5 Nov 2024 09:45:47 -0700 Subject: [PATCH 12/14] fix: NanoTDF secure key from debug logging and iv conflict risk (#208) This change is motivated from the CodeQL result: https://github.com/opentdf/java-sdk/security/code-scanning/1 Although that use of a static IV is deliberate, it helped highlight that we should ensure that there is no reuse of the IV when encrypting the data. In addition it was found that there were two places the key was logged, due to the sensitivity of the key this has been removed. --- sdk/src/main/java/io/opentdf/platform/sdk/NanoTDF.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) 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 75d0e734..39170b97 100644 --- a/sdk/src/main/java/io/opentdf/platform/sdk/NanoTDF.java +++ b/sdk/src/main/java/io/opentdf/platform/sdk/NanoTDF.java @@ -88,7 +88,6 @@ public int createNanoTDF(ByteBuffer data, OutputStream outputStream, MessageDigest digest = MessageDigest.getInstance("SHA-256"); byte[] hashOfSalt = digest.digest(MAGIC_NUMBER_AND_VERSION); byte[] key = ECKeyPair.calculateHKDF(hashOfSalt, symmetricKey); - logger.debug("createNanoTDF key is - {}", Base64.getEncoder().encodeToString(key)); // Encrypt policy PolicyObject policyObject = createPolicyObject(nanoTDFConfig.attributes); @@ -135,9 +134,11 @@ public int createNanoTDF(ByteBuffer data, OutputStream outputStream, // Encrypt the data byte[] actualIV = new byte[kIvPadding + kNanoTDFIvSize]; - byte[] iv = new byte[kNanoTDFIvSize]; - SecureRandom.getInstanceStrong().nextBytes(iv); - System.arraycopy(iv, 0, actualIV, kIvPadding, iv.length); + do { + byte[] iv = new byte[kNanoTDFIvSize]; + SecureRandom.getInstanceStrong().nextBytes(iv); + 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 byte[] cipherData = gcm.encrypt(actualIV, authTagSize, data.array(), 0, dataSize); @@ -173,7 +174,6 @@ public void readNanoTDF(ByteBuffer nanoTDF, OutputStream outputStream, byte[] key = kas.unwrapNanoTDF(header.getECCMode().getEllipticCurveType(), base64HeaderData, kasUrl); - logger.debug("readNanoTDF key is {}", Base64.getEncoder().encodeToString(key)); byte[] payloadLengthBuf = new byte[4]; nanoTDF.get(payloadLengthBuf, 1, 3); From 332511402d9cdaf57e3e25f5030dbc0107994d9b Mon Sep 17 00:00:00 2001 From: sujankota Date: Wed, 6 Nov 2024 11:49:57 -0500 Subject: [PATCH 13/14] feat(cmdline): assertions cli support (#204) Co-authored-by: Dave Mihalcik Co-authored-by: Elizabeth Healy <35498075+elizabethhealy@users.noreply.github.com> --- .../java/io/opentdf/platform/Command.java | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/cmdline/src/main/java/io/opentdf/platform/Command.java b/cmdline/src/main/java/io/opentdf/platform/Command.java index f8b37d0e..513c226b 100644 --- a/cmdline/src/main/java/io/opentdf/platform/Command.java +++ b/cmdline/src/main/java/io/opentdf/platform/Command.java @@ -1,9 +1,11 @@ package io.opentdf.platform; +import com.google.gson.JsonSyntaxException; import com.nimbusds.jose.JOSEException; import io.opentdf.platform.sdk.*; import io.opentdf.platform.sdk.TDF; +import com.google.gson.Gson; import org.apache.commons.codec.DecoderException; import picocli.CommandLine; import picocli.CommandLine.HelpCommand; @@ -64,7 +66,9 @@ void encrypt( @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) + @Option(names = { "--mime-type" }, defaultValue = Option.NULL_VALUE) Optional mimeType, + @Option(names = { "--with-assertions" }, defaultValue = Option.NULL_VALUE) Optional assertion) + throws IOException, JOSEException, AutoConfigureException, InterruptedException, ExecutionException { var sdk = buildSDK(); @@ -79,6 +83,21 @@ void encrypt( metadata.map(Config::withMetaData).ifPresent(configs::add); autoconfigure.map(Config::withAutoconfigure).ifPresent(configs::add); mimeType.map(Config::withMimeType).ifPresent(configs::add); + + if (assertion.isPresent()) { + var assertionConfig = assertion.get(); + Gson gson = new Gson(); + + AssertionConfig[] assertionConfigs; + try { + assertionConfigs = gson.fromJson(assertionConfig, AssertionConfig[].class); + } catch (JsonSyntaxException e) { + throw new RuntimeException("Failed to parse assertion, expects an list of assertions", e); + } + + configs.add(Config.withAssertionConfig(assertionConfigs)); + } + if (attributes.isPresent()) { configs.add(Config.withDataAttributes(attributes.get().split(","))); } From 0d6e7616f0d57d461a0f9002f395f8e2c7365cd3 Mon Sep 17 00:00:00 2001 From: dominic reed Date: Thu, 7 Nov 2024 10:47:51 -0800 Subject: [PATCH 14/14] fix(sdk): uses offset for ByteBuffer array offset (#209) ByteBuffer could have an array offset. We consider the length of the ByteBuffer, but we do not consider the offset and default it to 0. This PR fixes this problem by adding in the array offset when encrypting. --- sdk/src/main/java/io/opentdf/platform/sdk/NanoTDF.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 39170b97..5695713a 100644 --- a/sdk/src/main/java/io/opentdf/platform/sdk/NanoTDF.java +++ b/sdk/src/main/java/io/opentdf/platform/sdk/NanoTDF.java @@ -140,7 +140,7 @@ public int createNanoTDF(ByteBuffer data, OutputStream outputStream, 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 - byte[] cipherData = gcm.encrypt(actualIV, authTagSize, data.array(), 0, dataSize); + byte[] cipherData = gcm.encrypt(actualIV, authTagSize, data.array(), data.arrayOffset(), dataSize); // Write the length of the payload as int24 int cipherDataLengthWithoutPadding = cipherData.length - kIvPadding;