diff --git a/.gitignore b/.gitignore index 3792296b..cf534eee 100644 --- a/.gitignore +++ b/.gitignore @@ -29,3 +29,7 @@ xtest/sdk/java/cmdline.jar /xtest/java-sdk/ /xtest/sdk/go/otdfctl /xtest/otdfctl/ +/xtest/OpenTDFKit/ +/xtest/sdk/swift/Package.resolved +/xtest/sdk/swift/.build/ +/xtest/OpenTDFKit/ diff --git a/xtest/README.md b/xtest/README.md index 7c9cfcec..1ad33a7b 100644 --- a/xtest/README.md +++ b/xtest/README.md @@ -30,7 +30,7 @@ mv cmdline/target/cmdline.jar ../sdk/java/cmdline.jar #### Go SDK wrapped by otdfctl ```shell -git clone https://github.com/opentdf/platform +git clone https://github.com/opentdf/platform.git git clone https://github.com/opentdf/otdfctl.git cd otdfctl go mod edit -replace github.com/opentdf/platform/protocol/go=../platform/protocol/go @@ -40,6 +40,35 @@ go build . mv otdfctl ../sdk/go/otdfctl ``` +#### (External) Swift SDK - OpenTDFKit + +##### Prerequisite + +Xcode 16 + +##### Clone + +```shell +git clone https://github.com/arkavo-org/OpenTDFKit.git +``` + +##### Build + +```shell +cd sdk/swift +swift build --configuration release +``` + +##### Development + +```shell +brew install swiftformat +``` + +```shell +swiftformat --swiftversion 6.0 . +``` + ### Platform Backend 1. **Initialize Platform Configuration** @@ -61,13 +90,13 @@ mv otdfctl ../sdk/go/otdfctl ```shell go run ./service provision keycloak ``` -4. **Add Sample Attributes and Metadata** +4. **Start Server in Background** ```shell - go run ./service provision fixtures + go run ./service start ``` -5. **Start Server in Background** +5. **Add Sample Attributes and Metadata** ```shell - go run ./service start + go run ./service provision fixtures ``` ## Testing with Released Software @@ -90,3 +119,10 @@ pytest rm -rf tmp pytest test_tdfs.py ``` + +#### Run NanoTDF Tests + +```shell +rm -rf tmp +pytest test_tdfs.py --containers nano +``` diff --git a/xtest/conftest.py b/xtest/conftest.py index 800e9776..e1fe2d44 100644 --- a/xtest/conftest.py +++ b/xtest/conftest.py @@ -4,6 +4,7 @@ import string import abac +from xtest.tdfs import SUPPORTED_SDKS def pytest_addoption(parser): @@ -33,7 +34,7 @@ def pytest_generate_tests(metafunc): elif metafunc.config.getoption("--sdks"): encrypt_sdks = metafunc.config.getoption("--sdks").split() else: - encrypt_sdks = ["js", "go", "java"] + encrypt_sdks = SUPPORTED_SDKS metafunc.parametrize("encrypt_sdk", encrypt_sdks) if "decrypt_sdk" in metafunc.fixturenames: if metafunc.config.getoption("--sdks-decrypt"): @@ -41,7 +42,7 @@ def pytest_generate_tests(metafunc): elif metafunc.config.getoption("--sdks"): decrypt_sdks = metafunc.config.getoption("--sdks").split() else: - decrypt_sdks = ["js", "go", "java"] + decrypt_sdks = SUPPORTED_SDKS metafunc.parametrize("decrypt_sdk", decrypt_sdks) if "container" in metafunc.fixturenames: if metafunc.config.getoption("--containers"): diff --git a/xtest/nano.py b/xtest/nano.py index 3feeda43..8a2dca18 100644 --- a/xtest/nano.py +++ b/xtest/nano.py @@ -20,10 +20,10 @@ def to_s(n: int) -> str: def enc_hex_w_comment(B: bytes, c: str) -> str: - hex = enc_hex(B) - if len(hex) < 60: - return f"{hex} # {c}" - return f"\n# {c}\n{hex}" + hexes = enc_hex(B) + if len(hexes) < 60: + return f"{hexes} # {c}" + return f"\n# {c}\n{hexes}" def dec_hex(hexes: str) -> bytes: @@ -64,12 +64,10 @@ def bit_length(self) -> int: @property def byte_length(self) -> int: - # return int(math.ceil(self.bit_length / 8.0)) return (self.bit_length + 7) >> 3 @property def signature_length(self) -> int: - # return self.byte_length * 2 return self.byte_length << 1 @property @@ -301,7 +299,7 @@ def embedded_policy(embedded: str) -> Policy: def encrypted_policy(encrypted: bytes) -> Policy: return Policy( - policy_type=(PolicyType.ENCRYPTED), + policy_type=PolicyType.ENCRYPTED, encrypted_length=len(encrypted), encrypted=encrypted, embedded=None, diff --git a/xtest/sdk/swift/Package.swift b/xtest/sdk/swift/Package.swift new file mode 100644 index 00000000..074e9b25 --- /dev/null +++ b/xtest/sdk/swift/Package.swift @@ -0,0 +1,31 @@ +// swift-tools-version:6.0 +import PackageDescription + +let package = Package( + name: "cli", + platforms: [ + .macOS(.v10_15), + ], + products: [ + .executable( + name: "cli", + targets: ["cli"] + ), + ], + dependencies: [ + .package(url: "https://github.com/arkavo-org/OpenTDFKit.git", exact: "2.0.1"), + .package(url: "https://github.com/apple/swift-argument-parser.git", from: "1.5.0"), + ], + targets: [ + .executableTarget( + name: "cli", + dependencies: [ + .product(name: "ArgumentParser", package: "swift-argument-parser"), + ], + path: "./", + exclude: [ + "cli.sh", + ] + ), + ] +) diff --git a/xtest/sdk/swift/cli.sh b/xtest/sdk/swift/cli.sh new file mode 100755 index 00000000..64b8eddc --- /dev/null +++ b/xtest/sdk/swift/cli.sh @@ -0,0 +1,55 @@ +#!/usr/bin/env bash +# shellcheck disable=SC2206,SC1091 + +# Common shell wrapper used to interface to SDK implementation. +# +# Usage: ./cli.sh +# +SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd) + +# shellcheck source=../../test.env +source "$SCRIPT_DIR"/../../test.env + +args=( + -o "$3" + --host "$PLATFORMURL" + --tls-no-verify + --log-level debug + --with-client-creds '{"clientId":"'$CLIENTID'","clientSecret":"'$CLIENTSECRET'"}' +) +if [ "$4" == "nano" ]; then + args+=(--tdf-type "$4") +fi + +if [ -n "$5" ]; then + args+=(--mime-type "$5") +fi + +if [ -n "$6" ]; then + args+=(--attr "$6") +fi + +if [ -z "$SCRIPT_DIR" ]; then + echo "Error: SCRIPT_DIR is not set." + exit 1 +fi + +CMD_PATH="$SCRIPT_DIR"/.build/release/cli +cmd=("$CMD_PATH") + +if [ "$1" == "encrypt" ]; then + echo "${cmd[@]}" encrypt "${args[@]}" "$2" + if ! "${cmd[@]}" encrypt "${args[@]}" "$2"; then + exit 1 + fi + if [ -f "${3}.tdf" ]; then + # go helpfully adds a tdf extension to all files + mv "${3}.tdf" "${3}" + fi +elif [ "$1" == "decrypt" ]; then + echo "${cmd[@]}" decrypt "${args[@]}" "$2" + "${cmd[@]}" decrypt "${args[@]}" "$2" +else + echo "Incorrect argument provided" + exit 1 +fi diff --git a/xtest/sdk/swift/main.swift b/xtest/sdk/swift/main.swift new file mode 100644 index 00000000..a70cdef3 --- /dev/null +++ b/xtest/sdk/swift/main.swift @@ -0,0 +1,91 @@ +import ArgumentParser + import Foundation + + @main + struct CLI: ParsableCommand { + @Argument(help: "Operation to perform (encrypt/decrypt)") + var operation: String + + @Option(name: .shortAndLong, help: "Output file") + var outputFile: String + + @Option(name: .long, help: "Platform URL") + var host: String + + @Flag(name: .long, help: "Disable TLS verification") + var tlsNoVerify: Bool = false + + @Option(name: .long, help: "Log level") + var logLevel: String = "info" + + @Option(name: .long, help: "Client credentials in JSON format") + var withClientCreds: String + + @Option(name: .long, help: "TDF type") + var tdfType: String? + + @Option(name: .long, help: "MIME type") + var mimeType: String? + + @Option(name: .long, help: "Attribute") + var attr: String? + + @Argument(help: "Input file") + var inputFile: String + + func run() throws { + var args: [String] = [ + operation, + "-o", outputFile, + "--host", host, + "--log-level", logLevel, + "--with-client-creds", withClientCreds, + ] + + if tlsNoVerify { + args.append("--tls-no-verify") + } + + if let tdfType { + args.append(contentsOf: ["--tdf-type", tdfType]) + } + + if let mimeType { + args.append(contentsOf: ["--mime-type", mimeType]) + } + + if let attr { + args.append(contentsOf: ["--attr", attr]) + } + + // Append the input file at the end of arguments + args.append(inputFile) + + let cmd = ["./.build/release/cli"] + args + + print(cmd) + runCommand(cmd) + } + + func runCommand(_ command: [String]) { + let process = Process() + process.executableURL = URL(fileURLWithPath: command[0]) + process.arguments = Array(command.dropFirst()) + + do { + try process.run() + process.waitUntilExit() + + if process.terminationStatus != 0 { + Foundation.exit(1) + } + + if operation == "encrypt", FileManager.default.fileExists(atPath: "\(outputFile).tdf") { + try FileManager.default.moveItem(atPath: "\(outputFile).tdf", toPath: outputFile) + } + } catch { + print("Error: \(error)") + Foundation.exit(1) + } + } + } diff --git a/xtest/tdfs.py b/xtest/tdfs.py index d9979e8b..33436dd8 100644 --- a/xtest/tdfs.py +++ b/xtest/tdfs.py @@ -4,14 +4,14 @@ import zipfile from pydantic import BaseModel -from typing import Literal +from typing import Literal, get_args logger = logging.getLogger("xtest") logging.basicConfig() logging.getLogger().setLevel(logging.DEBUG) - -sdk_type = Literal["go", "java", "js"] +sdk_type = Literal["js", "go", "java", "swift"] +SUPPORTED_SDKS = list(get_args(sdk_type)) feature_type = Literal["autoconfigure", "nano_ecdsa", "ns_grants"] @@ -19,6 +19,7 @@ "go": "sdk/go/cli.sh", "java": "sdk/java/cli.sh", "js": "sdk/js/cli/cli.sh", + "swift": "sdk/swift/cli.sh", } @@ -98,9 +99,11 @@ def encrypt( ct_file, mime_type="application/octet-stream", fmt="nano", - attr_values=[], + attr_values=None, use_ecdsa_binding=False, ): + if attr_values is None: + attr_values = [] c = [ sdk_paths[sdk], "encrypt",